From 4065c946a084fa7782c7abffab61889a5c066a3e Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sat, 29 Jul 2023 17:47:26 +0200 Subject: [PATCH 403/464] bes2600: Add the wifi driver for Pinetab2 WiFi chip (0.3.10_2022.1125) Import from https://gitlab.com/TuxThePenguin0/bes2600 Version 0.3.10_2022.1125 from PineTab2 Android 11 RK BSP release. Signed-off-by: Ondrej Jirman --- drivers/staging/bes2600/Kconfig | 119 + drivers/staging/bes2600/Makefile | 321 ++ drivers/staging/bes2600/ap.c | 1984 ++++++++ drivers/staging/bes2600/ap.h | 58 + drivers/staging/bes2600/bes2600.h | 850 ++++ drivers/staging/bes2600/bes2600_btusb.c | 633 +++ drivers/staging/bes2600/bes2600_cfgvendor.c | 1063 ++++ drivers/staging/bes2600/bes2600_cfgvendor.h | 218 + drivers/staging/bes2600/bes2600_driver_mode.h | 65 + drivers/staging/bes2600/bes2600_factory.c | 930 ++++ drivers/staging/bes2600/bes2600_factory.h | 208 + drivers/staging/bes2600/bes2600_log.c | 152 + drivers/staging/bes2600/bes2600_log.h | 161 + drivers/staging/bes2600/bes2600_plat.h | 51 + drivers/staging/bes2600/bes2600_sdio.c | 2563 ++++++++++ drivers/staging/bes2600/bes2600_spi.c | 1803 +++++++ drivers/staging/bes2600/bes2600_usb.c | 1554 ++++++ drivers/staging/bes2600/bes_chardev.c | 1280 +++++ drivers/staging/bes2600/bes_chardev.h | 70 + drivers/staging/bes2600/bes_fw.c | 1511 ++++++ drivers/staging/bes2600/bes_fw_common.c | 125 + drivers/staging/bes2600/bes_fw_common.h | 103 + drivers/staging/bes2600/bes_fw_usb.c | 412 ++ .../bes2600/bes_nl80211_testmode_msg.h | 258 + drivers/staging/bes2600/bes_pwr.c | 1406 +++++ drivers/staging/bes2600/bes_pwr.h | 167 + drivers/staging/bes2600/bh.c | 1654 ++++++ drivers/staging/bes2600/bh.h | 53 + drivers/staging/bes2600/debug.c | 784 +++ drivers/staging/bes2600/debug.h | 191 + drivers/staging/bes2600/epta_coex.c | 584 +++ drivers/staging/bes2600/epta_coex.h | 86 + drivers/staging/bes2600/epta_request.c | 650 +++ drivers/staging/bes2600/epta_request.h | 52 + drivers/staging/bes2600/fwio.c | 55 + drivers/staging/bes2600/fwio.h | 48 + drivers/staging/bes2600/ht.h | 43 + drivers/staging/bes2600/hwio.c | 368 ++ drivers/staging/bes2600/hwio.h | 364 ++ drivers/staging/bes2600/itp.c | 744 +++ drivers/staging/bes2600/itp.h | 152 + drivers/staging/bes2600/main.c | 1101 ++++ drivers/staging/bes2600/pm.c | 474 ++ drivers/staging/bes2600/pm.h | 35 + drivers/staging/bes2600/queue.c | 931 ++++ drivers/staging/bes2600/queue.h | 173 + drivers/staging/bes2600/sbus.h | 105 + drivers/staging/bes2600/scan.c | 1180 +++++ drivers/staging/bes2600/scan.h | 72 + drivers/staging/bes2600/sta.c | 4514 +++++++++++++++++ drivers/staging/bes2600/sta.h | 146 + drivers/staging/bes2600/tx_loop.c | 530 ++ drivers/staging/bes2600/tx_loop.h | 33 + drivers/staging/bes2600/txrx.c | 1995 ++++++++ drivers/staging/bes2600/txrx.h | 99 + drivers/staging/bes2600/txrx_opt.c | 467 ++ drivers/staging/bes2600/txrx_opt.h | 31 + drivers/staging/bes2600/wifi_testmode_cmd.c | 143 + drivers/staging/bes2600/wsm.c | 3132 ++++++++++++ drivers/staging/bes2600/wsm.h | 2211 ++++++++ 60 files changed, 41265 insertions(+) create mode 100644 drivers/staging/bes2600/Kconfig create mode 100644 drivers/staging/bes2600/Makefile create mode 100644 drivers/staging/bes2600/ap.c create mode 100644 drivers/staging/bes2600/ap.h create mode 100644 drivers/staging/bes2600/bes2600.h create mode 100644 drivers/staging/bes2600/bes2600_btusb.c create mode 100644 drivers/staging/bes2600/bes2600_cfgvendor.c create mode 100644 drivers/staging/bes2600/bes2600_cfgvendor.h create mode 100644 drivers/staging/bes2600/bes2600_driver_mode.h create mode 100644 drivers/staging/bes2600/bes2600_factory.c create mode 100644 drivers/staging/bes2600/bes2600_factory.h create mode 100644 drivers/staging/bes2600/bes2600_log.c create mode 100644 drivers/staging/bes2600/bes2600_log.h create mode 100644 drivers/staging/bes2600/bes2600_plat.h create mode 100644 drivers/staging/bes2600/bes2600_sdio.c create mode 100644 drivers/staging/bes2600/bes2600_spi.c create mode 100644 drivers/staging/bes2600/bes2600_usb.c create mode 100644 drivers/staging/bes2600/bes_chardev.c create mode 100644 drivers/staging/bes2600/bes_chardev.h create mode 100644 drivers/staging/bes2600/bes_fw.c create mode 100644 drivers/staging/bes2600/bes_fw_common.c create mode 100644 drivers/staging/bes2600/bes_fw_common.h create mode 100644 drivers/staging/bes2600/bes_fw_usb.c create mode 100644 drivers/staging/bes2600/bes_nl80211_testmode_msg.h create mode 100644 drivers/staging/bes2600/bes_pwr.c create mode 100644 drivers/staging/bes2600/bes_pwr.h create mode 100644 drivers/staging/bes2600/bh.c create mode 100644 drivers/staging/bes2600/bh.h create mode 100644 drivers/staging/bes2600/debug.c create mode 100644 drivers/staging/bes2600/debug.h create mode 100644 drivers/staging/bes2600/epta_coex.c create mode 100644 drivers/staging/bes2600/epta_coex.h create mode 100644 drivers/staging/bes2600/epta_request.c create mode 100644 drivers/staging/bes2600/epta_request.h create mode 100644 drivers/staging/bes2600/fwio.c create mode 100644 drivers/staging/bes2600/fwio.h create mode 100644 drivers/staging/bes2600/ht.h create mode 100644 drivers/staging/bes2600/hwio.c create mode 100644 drivers/staging/bes2600/hwio.h create mode 100644 drivers/staging/bes2600/itp.c create mode 100644 drivers/staging/bes2600/itp.h create mode 100644 drivers/staging/bes2600/main.c create mode 100644 drivers/staging/bes2600/pm.c create mode 100644 drivers/staging/bes2600/pm.h create mode 100644 drivers/staging/bes2600/queue.c create mode 100644 drivers/staging/bes2600/queue.h create mode 100644 drivers/staging/bes2600/sbus.h create mode 100644 drivers/staging/bes2600/scan.c create mode 100644 drivers/staging/bes2600/scan.h create mode 100644 drivers/staging/bes2600/sta.c create mode 100644 drivers/staging/bes2600/sta.h create mode 100644 drivers/staging/bes2600/tx_loop.c create mode 100644 drivers/staging/bes2600/tx_loop.h create mode 100644 drivers/staging/bes2600/txrx.c create mode 100644 drivers/staging/bes2600/txrx.h create mode 100644 drivers/staging/bes2600/txrx_opt.c create mode 100644 drivers/staging/bes2600/txrx_opt.h create mode 100644 drivers/staging/bes2600/wifi_testmode_cmd.c create mode 100644 drivers/staging/bes2600/wsm.c create mode 100644 drivers/staging/bes2600/wsm.h diff --git a/drivers/staging/bes2600/Kconfig b/drivers/staging/bes2600/Kconfig new file mode 100644 index 000000000000..374c15db823f --- /dev/null +++ b/drivers/staging/bes2600/Kconfig @@ -0,0 +1,119 @@ +config BES2600 + tristate "BES2600 WLAN support" + select MAC80211 + select CFG80211 + select NL80211_TESTMODE + default m + ---help--- + This is an experimental driver for the bes2600 chip-set. + Enabling this option enables the generic driver without + any platform support. + +if BES2600 + +config BES2600_WLAN_SDIO + bool "SDIO bus interface support" + depends on MMC + default y + ---help--- + This option enables the SDIO bus interface support for Bes2600 + WLAN driver. Say Y if you want to use SDIO. + +config BES2600_WLAN_USB + bool "USB bus interface support" + depends on USB + default n + ---help--- + This option enables the USB bus interface support for Bes2600 + WLAN driver. Say Y if you want to use USB. + +config BES2600_WLAN_SPI + bool "SPI bus interface support" + depends on SPI + default n + ---help--- + This option enables the SPI bus interface support for Bes2600 + WLAN driver. Say Y if you want to use USB. + +config BES2600_USE_GPIO_IRQ + bool "Use GPIO interrupt" + default n + ---help--- + Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ. + If unsure, say N. + +config BES2600_5GHZ_SUPPORT + bool "5GHz band support" + default y + ---help--- + Say Y if your device supports 5GHz band. + If unsure, say N. + +config BES2600_WAPI_SUPPORT + bool "WAPI support" + default y + ---help--- + Say Y if your compat-wireless support WAPI. + If unsure, say N. + +config BES2600_USE_STE_EXTENSIONS + bool "STE extensions" + default n + ---help--- + Say Y if you want to include STE extensions. + If unsure, say N. + +config BES2600_DISABLE_BEACON_HINTS + bool "Disable 11d beacon hints" + default n + ---help--- + Say Y if you want to disable 11d beacon hints. + If unsure, say N. + +config BES2600_TESTMODE + bool "bes2600 testmode support" + select NL80211_TESTMODE + default n + ---help--- + Say Y if you want to enable bes2600 testmode. + If unsure, say N. + +menu "Driver debug features" + +config BES2600_DEBUGFS + bool "Expose driver internals to DebugFS (DEVELOPMENT)" + default y + +config BES2600_BH_DEBUG + bool "Enable low-level device communication logs (DEVELOPMENT)" + +config BES2600_WSM_DEBUG + bool "Enable WSM API debug messages (DEVELOPMENT)" + +config BES2600_WSM_DUMPS + bool "Verbose WSM API logging (DEVELOPMENT)" + +config BES2600_WSM_DUMPS_SHORT + bool "Dump only first x bytes (default 20) (DEVELOPMENT)" + +config BES2600_TXRX_DEBUG + bool "Enable TX/RX debug messages (DEVELOPMENT)" + +config BES2600_TX_POLICY_DEBUG + bool "Enable TX policy debug (DEVELOPMENT)" + +config BES2600_STA_DEBUG + bool "Enable STA/AP debug (DEVELOPMENT)" + +config BES2600_DUMP_ON_ERROR + bool "Dump kernel in case of critical error (DEVELOPMENT)" + +config BES2600_ITP + bool "Enable ITP DebugFS" + ---help--- + Say Y if you want to include ITP code. + If unsure, say N. + +endmenu + +endif # BES2600 diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile new file mode 100644 index 000000000000..103519907e4f --- /dev/null +++ b/drivers/staging/bes2600/Makefile @@ -0,0 +1,321 @@ +# feature option +BES2600 ?= m + +CONFIG_BES2600_WLAN_SDIO ?= y +CONFIG_BES2600_WLAN_SPI ?= n +CONFIG_BES2600_WLAN_USB ?= n + +CONFIG_BES2600_VENDOR_CMD ?= y +ifneq ($(CONFIG_BES2600_WLAN_USB),y) +CONFIG_BES2600_TESTMODE ?= y +endif + +CONFIG_BES2600_WAPI_SUPPORT ?= y +CONFIG_BES2600_5GHZ_SUPPORT ?= y +CONFIG_BES2600_STA_DEBUG ?= y +CONFIG_BES2600_STATIC_SDD ?= y +P2P_MULTIVIF ?= y +AP_AGGREGATE_FW_FIX ?= y +CONFIG_BES2600_BT ?= n +WIFI_BT_COEXIST_EPTA_ENABLE ?= y +WIFI_BT_COEXIST_EPTA_FDD ?= n +ifneq ($(CONFIG_BES2600_WLAN_USB),y) +CONFIG_BES2600_WOWLAN ?= y +endif +CONFIG_BES2600_LISTEN_INTERVAL ?= 3 +BSS_LOSS_CHECK ?= y + +CONFIG_BES2600_DEBUGFS ?= y +CONFIG_BES2600_ITP ?= n + +CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES ?= n +CONFIG_BES2600_CALIB_FROM_LINUX ?= y + +CONFIG_BES2600_WIFI_BOOT_ON ?= n +CONFIG_BES2600_BT_BOOT_ON ?= n + +PLAT_ALLWINNER_T507 ?= n +PLAT_ALLWINNER_R329 ?= n +PLAT_ROCKCHIP ?= y +PLAT_UBUNTU_VUB300 ?= n +PLAT_QCOM_QM215 ?= n + +BES2600_GPIO_WAKEUP_AP ?= y +BES2600_WRITE_DPD_TO_FILE ?= n + +# bes evb +BES2600_INDEPENDENT_EVB ?= n +# wifi module for allwinner made by bes +BES2600_INTEGRATED_MODULE_V1 ?= n +# xiaomi R329 wifi module +BES2600_INTEGRATED_MODULE_V2 ?= n +# sicun QM215 wifi module +BES2600_INTEGRATED_MODULE_V3 ?= n + +# 0: use dynamic_ps_timeout value +# other: override dynamic_ps_timeout value +BES2600_FASTPS_IDLE_TIME ?= 0 + +#1.rock api(fixed macaddr) +#2.read from file(macaddr of customers) +#3.read from file(macaddr template) +#4.random macaddr +GET_MAC_ADDR_METHOD ?= 4 + +ifeq ($(CONFIG_BES2600_DEBUGFS),y) +BES2600_DUMP_FW_DPD_LOG ?= n +endif + +ifeq ($(CONFIG_BES2600_TESTMODE),y) +CONFIG_BES2600_KEEP_ALIVE ?= n +ifeq ($(CONFIG_BES2600_KEEP_ALIVE)_$(PLAT_ALLWINNER_R329),y_y) +ccflags-y += -DVENDOR_XM_KEEPALIVE +endif +endif + +BES2600_DRV_VERSION := bes2600_0.3.10_2022.1125 + +ifeq ($(GET_MAC_ADDR_METHOD)_$(PLAT_ROCKCHIP),1_n) + $(error Parameter setting error, GET_MAC_ADDR_METHOD can only be set to 1 on rockchip platform) +endif + +ifeq ($(BES2600_GPIO_WAKEUP_AP)_$(CONFIG_BES2600_WLAN_SDIO),y_n) + $(error Parameter setting error, BES2600_GPIO_WAKEUP_AP can only be set to y when CONFIG_BES2600_WLAN_SDIO set to y) +endif + +ifeq ($(GET_MAC_ADDR_METHOD)_$(PLAT_ALLWINNER_R329),2_y) +PATH_WIFI_MACADDR ?= /data/wifi/wifimac.txt +endif + +ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y) +FACTORY_CRC_CHECK ?= n +FACTORY_SAVE_MULTI_PATH ?= n +ifeq ($(FACTORY_SAVE_MULTI_PATH),y) +FACTORY_PATH ?= /data/cfg/bes2600_factory.txt +FACTORY_DEFAULT_PATH ?= /lib/firmware/bes2600_factory.txt +else ifneq ($(filter y,$(PLAT_ALLWINNER_R329) $(PLAT_QCOM_QM215)),) +FACTORY_PATH ?= /data/wifi/bes2600_factory.txt +else ifeq ($(PLAT_ALLWINNER_T507),y) +FACTORY_PATH ?= /vendor/firmware/bes2600_factory.txt +endif +endif + +FACTORY_PATH ?= /vendor/lib/firmware/bes2600_factory.txt + +# basic function +define boolen_flag +$(strip $(if $(findstring $($(1)),$(2)),-D$(1))) +endef + +define string_flag +$(strip $(if $($(1)),-D$(1)=\"$($(1))\")) +endef + +define value_flag +$(strip $(if $($(1)),-D$(1)=$($(1)))) +endef + +ccflags-y += -Werror + +ifneq ($(filter y,$(PLAT_ALLWINNER_R329) $(PLAT_ALLWINNER_T507)),) +ccflags-y += -DPLAT_ALLWINNER +endif + +ifeq ($(CONFIG_BES2600_WLAN_SDIO),y) +FW_DOWNLOAD_BY_SDIO ?= y +SDIO_HOST_ADMA_SUPPORT ?= y +ccflags-y += -DCONFIG_BES2600_WLAN_BES +ccflags-y += -DBES_SDIO_RXTX_TOGGLE +ccflags-y += -DBES_SDIO_TX_MULTIPLE_ENABLE +ccflags-y += -DBES_SDIO_RX_MULTIPLE_ENABLE +ccflags-y += -DBES_UNIFIED_PM +ccflags-y += -DBES_SDIO_OPTIMIZED_LEN +ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG +#specific for xiaomi R329 +#ccflags-y += -DBES2600_BOOT_UART_TO_SDIO +endif # CONFIG_BES2600_WLAN_SDIO + +ifeq ($(CONFIG_BES2600_WLAN_SPI),y) +FW_DOWNLOAD_BY_UART ?= y +ccflags-y += -DCONFIG_BES2600_WLAN_BES +#ccflags-y += -DCONFIG_BES2600_SPI_THROUGHPUT_TEST +#ccflags-y += -DFW_DOWNLOAD_MANULLY +ccflags-y += -DBES_UNIFIED_PM +endif # CONFIG_BES2600_WLAN_SPI + +ifeq ($(CONFIG_BES2600_WLAN_USB),y) +FW_DOWNLOAD_BY_USB ?= n +ccflags-y += -DCONFIG_BES2600_WLAN_BES +endif # CONFIG_BES2600_WLAN_USB + +ifeq ($(FW_DOWNLOAD_BY_UART)_$(PLAT_ALLWINNER_T507),y_y) +BES2600_LOAD_FW_TOOL_PATH ?= /system/bin/bes_fw_download +BES2600_LOAD_FW_TOOL_DEVICE ?= /dev/ttyS2 +endif + +ifeq ($(FW_DOWNLOAD_BY_UART)_$(PLAT_QCOM_QM215),y_y) +FW_DOWNLOAD_UART_DAEMON ?= y +BES2600_LOAD_FW_TOOL_PATH ?= /system/bin/bes_fw_download +BES2600_LOAD_FW_TOOL_DEVICE ?= /dev/ttyHS0 +endif + +ifeq ($(BES2600_WRITE_DPD_TO_FILE),y) +BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin +BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin +BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin +endif + +ifeq ($(BES2600_DUMP_FW_DPD_LOG),y) +BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log +endif + +# compilation macros setting +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WLAN_SDIO,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WLAN_SPI,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WLAN_USB,y) + +ccflags-y += $(call boolen_flag,PLAT_ALLWINNER_R329,y) +ccflags-y += $(call boolen_flag,PLAT_ALLWINNER_T507,y) +ccflags-y += $(call boolen_flag,PLAT_UBUNTU_VUB300,y) +ccflags-y += $(call boolen_flag,PLAT_ROCKCHIP,y) +ccflags-y += $(call boolen_flag,PLAT_QCOM_QM215,y) + +ccflags-y += $(call boolen_flag,CONFIG_BES2600_VENDOR_CMD,y) + +ccflags-y += $(call boolen_flag,CONFIG_BES2600_5GHZ_SUPPORT,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_STATIC_SDD,y) +ccflags-y += $(call boolen_flag,P2P_MULTIVIF,y) +ccflags-y += $(call boolen_flag,AP_AGGREGATE_FW_FIX,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_BT,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WAPI_SUPPORT,y) +ccflags-y += $(call boolen_flag,WIFI_BT_COEXIST_EPTA_ENABLE,y) +ccflags-y += $(call boolen_flag,WIFI_BT_COEXIST_EPTA_FDD,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_DISABLE_BEACON_HINTS,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES,y) + +ccflags-y += $(call boolen_flag,CONFIG_BES2600_ITP,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_DEBUGFS,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_BH_DEBUG,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DEBUG,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DUMPS,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DUMPS_SHORT,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_TXRX_DEBUG,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_TX_POLICY_DEBUG,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_STA_DEBUG,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_DUMP_ON_ERROR,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WOWLAN,y) +ccflags-y += $(call value_flag,CONFIG_BES2600_LISTEN_INTERVAL) +ccflags-y += $(call value_flag,BES2600_FASTPS_IDLE_TIME) +ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y) + +ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH) +ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE) +ccflags-y += $(call string_flag,BES2600_DRV_VERSION) +ccflags-y += $(call string_flag,BES2600_DPD_PATH) +ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH) +ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH) +ccflags-y += $(call boolen_flag,FW_DOWNLOAD_UART_DAEMON,y) + +ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y) +ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y) +ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V2,y) +ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V3,y) +ccflags-y += $(call boolen_flag,FW_DOWNLOAD_BY_SDIO,y) +ccflags-y += $(call boolen_flag,SDIO_HOST_ADMA_SUPPORT,y) +ccflags-y += $(call boolen_flag,FW_DOWNLOAD_BY_UART,y) +ccflags-y += $(call boolen_flag,FW_DOWNLOAD_BY_USB,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_TESTMODE,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_KEEP_ALIVE,y) + +ccflags-y += $(call boolen_flag,CONFIG_BES2600_WIFI_BOOT_ON,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_BT_BOOT_ON,y) +ccflags-y += $(call boolen_flag,CONFIG_BES2600_CALIB_FROM_LINUX,y) + +ccflags-y += $(call value_flag,GET_MAC_ADDR_METHOD) +ccflags-y += $(call string_flag,PATH_WIFI_MACADDR) +ccflags-y += $(call string_flag,FACTORY_PATH) +ccflags-y += $(call string_flag,FACTORY_DEFAULT_PATH) +ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y) +ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y) + +ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y) +ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y) + +ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y) +ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH) + + +# internal feature options +ccflags-y += -DAP_HT_CAP_UPDATE +ccflags-y += -DBES2600_RX_IN_BH +ccflags-y += -DDCW1260_DETECTION_LOGIC + +# sdd options +ccflags-y += -DTEST_11B=0 +ccflags-y += -DDPD_CALI=0 +ccflags-y += -DDPD_CALI=0 +ccflags-y += -DALI_CONFG=0 +ccflags-y += -DCHIP_WIFI_ROM_VER=1 +ccflags-y += -DWIFI_OUT_FEM=0 +ccflags-y += -DRF_TX_CONTROL_IO=16 + +#ccflags-y += -DP2P_STA_COEX +#ccflags-y += -DCUSTOM_FEATURE +#ccflags-y += -DMCAST_FWDING +#ccflags-y += -DAP_AGGREGATE_FW_FIX +# Extra IE for probe response from upper layer is needed in P2P GO +# For offloading probe response to FW, the extra IE must be included +# in the probe response template +#ccflags-y += -DPROBE_RESP_EXTRA_IE +#ccflags-y += -DIPV6_FILTERING + +# basic files for building module +bes2600-y := \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + ap.o \ + scan.o \ + txrx_opt.o \ + bes_chardev.o \ + tx_loop.o \ + bes2600_log.o + +# compilation files select +bes2600-$(CONFIG_BES2600_DEBUGFS) += debug.o +bes2600-$(CONFIG_BES2600_ITP) += itp.o +bes2600-$(CONFIG_PM) += pm.o +bes2600-$(CONFIG_BES2600_BT) += bes2600_btusb.o +bes2600-$(CONFIG_BES2600_TESTMODE) += wifi_testmode_cmd.o +bes2600-$(CONFIG_BES2600_WLAN_SDIO) += bes2600_sdio.o +bes2600-$(CONFIG_BES2600_WLAN_SPI) += bes2600_spi.o +bes2600-$(CONFIG_BES2600_WLAN_USB) += bes2600_usb.o +bes2600-$(CONFIG_BES2600_VENDOR_CMD) += bes2600_cfgvendor.o + +bes2600-$(FW_DOWNLOAD_BY_SDIO) += bes_fw.o bes_fw_common.o +bes2600-$(FW_DOWNLOAD_BY_UART) += bes_fw.o bes_fw_common.o +bes2600-$(FW_DOWNLOAD_BY_USB) += bes_fw_usb.o bes_fw_common.o +bes2600-$(WIFI_BT_COEXIST_EPTA_ENABLE) += epta_coex.o epta_request.o +bes2600-$(CONFIG_BES2600_WOWLAN) += bes_pwr.o + +ifneq ($(CONFIG_BES2600_CALIB_FROM_LINUX)_$(PLAT_ALLWINNER_R329),n_n) +bes2600-y += bes2600_factory.o +endif + +obj-$(BES2600) += bes2600.o + +ifeq ($(KERNELRELEASE),) +KVERSION = $(shell uname -r) +all: + make -C /lib/modules/$(KVERSION)/build M=$(shell pwd) modules +clean: + make -C /lib/modules/$(KVERSION)/build M=$(shell pwd) clean +endif + +clean: + rm -rf *.o .*.o.d *.ko *.o.ur-safe .*.cmd *.mod *.mod.c .cache.mk modules.order Module.symvers .tmp_versions diff --git a/drivers/staging/bes2600/ap.c b/drivers/staging/bes2600/ap.c new file mode 100644 index 000000000000..2126e7b516da --- /dev/null +++ b/drivers/staging/bes2600/ap.c @@ -0,0 +1,1984 @@ +/* + * mac80211 STA and AP API for mac80211 BES2600 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "bes2600.h" +#include "sta.h" +#include "ap.h" +#include "bh.h" +#include "net/mac80211.h" +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) +#include +#endif +#include "epta_request.h" +#include "epta_coex.h" +#include "txrx_opt.h" + +#ifdef AP_HT_CAP_UPDATE +#define HT_INFO_OFFSET 4 +#define HT_INFO_MASK 0x0011 +#define HT_INFO_IE_LEN 22 +#endif +#define BES2600_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +#define BES2600_ENABLE_ARP_FILTER_OFFLOAD 3 + +#define BES2600_KEEP_ALIVE_PERIOD (20) + +#ifndef ERP_INFO_BYTE_OFFSET +#define ERP_INFO_BYTE_OFFSET 2 +#endif + +#ifdef IPV6_FILTERING +#define BES2600_ENABLE_NDP_FILTER_OFFLOAD 3 +#endif /*IPV6_FILTERING*/ + +static int bes2600_upload_beacon(struct bes2600_vif *priv); +#ifdef PROBE_RESP_EXTRA_IE +static int bes2600_upload_proberesp(struct bes2600_vif *priv); +#endif +static int bes2600_upload_pspoll(struct bes2600_vif *priv); +static int bes2600_upload_null(struct bes2600_vif *priv); +static int bes2600_upload_qosnull(struct bes2600_vif *priv); +static int bes2600_start_ap(struct bes2600_vif *priv); +static int bes2600_update_beaconing(struct bes2600_vif *priv); +/* +static int bes2600_enable_beaconing(struct bes2600_vif *priv, + bool enable); +*/ +static void __bes2600_sta_notify(struct bes2600_vif *priv, + enum sta_notify_cmd notify_cmd, + int link_id); + +/* ******************************************************************** */ +/* AP API */ + +int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct bes2600_sta_priv *sta_priv = + (struct bes2600_sta_priv *)&sta->drv_priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_link_entry *entry; + struct sk_buff *skb; + struct bes2600_common *hw_priv = hw->priv; + +#ifdef P2P_MULTIVIF + WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); +#endif + + bes2600_info(BES2600_DBG_AP, "%s mode:%u, MFP:%u\n", + __FUNCTION__, priv->mode, sta->mfp); + + if (priv->mode == NL80211_IFTYPE_STATION) + priv->pmf = sta->mfp; + + if (priv->mode != NL80211_IFTYPE_AP) + return 0; + + sta_priv->priv = priv; + sta_priv->link_id = bes2600_find_link_id(priv, sta->addr); + if (WARN_ON(!sta_priv->link_id)) { + /* Impossible error */ + wiphy_info(priv->hw->wiphy, + "[AP] No more link IDs available.\n"); + return -ENOENT; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + priv->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = BES2600_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); +#ifdef AP_AGGREGATE_FW_FIX + hw_priv->connected_sta_cnt++; + if(hw_priv->connected_sta_cnt>1) { + wsm_lock_tx(hw_priv); + WARN_ON(wsm_set_block_ack_policy(hw_priv, + BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + priv->if_id)); + wsm_unlock_tx(hw_priv); + } +#endif + + return 0; +} + +int bes2600_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_sta_priv *sta_priv = + (struct bes2600_sta_priv *)&sta->drv_priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_link_entry *entry; + +#ifdef P2P_MULTIVIF + WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); +#endif + + bes2600_info(BES2600_DBG_AP, "%s mode:%u\n", __FUNCTION__, priv->mode); + + if (priv->mode == NL80211_IFTYPE_STATION) + priv->pmf = false; + + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = BES2600_LINK_RESERVE; + entry->timestamp = jiffies; + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + spin_unlock_bh(&priv->ps_state_lock); + flush_workqueue(hw_priv->workqueue); +#ifdef AP_AGGREGATE_FW_FIX + hw_priv->connected_sta_cnt--; + if(hw_priv->connected_sta_cnt <= 1) { + if ((priv->if_id != 1) || + ((priv->if_id == 1) && hw_priv->is_go_thru_go_neg)) { + wsm_lock_tx(hw_priv); + WARN_ON(wsm_set_block_ack_policy(hw_priv, + BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + priv->if_id)); + wsm_unlock_tx(hw_priv); + } + } +#endif + + return 0; +} + +static void __bes2600_sta_notify(struct bes2600_vif *priv, + enum sta_notify_cmd notify_cmd, + int link_id) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + u32 bit, prev; + + /* Zero link id means "for all link IDs" */ + if (link_id) + bit = BIT(link_id); + else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) + bit = 0; + else + bit = priv->link_id_map; + prev = priv->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(hw_priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && link_id && + !priv->sta_asleep_mask) + queue_work(hw_priv->workqueue, + &priv->multicast_stop_work); + bes2600_bh_wakeup(hw_priv); + } + break; + } +} + +void bes2600_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_sta_priv *sta_priv = + (struct bes2600_sta_priv *)&sta->drv_priv; + +#ifdef P2P_MULTIVIF + WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); +#endif + spin_lock_bh(&priv->ps_state_lock); + __bes2600_sta_notify(priv, notify_cmd, sta_priv->link_id); + spin_unlock_bh(&priv->ps_state_lock); +} + +static void bes2600_ps_notify(struct bes2600_vif *priv, + int link_id, bool ps) +{ + if (link_id > CW1250_MAX_STA_IN_AP_MODE) + return; + + bes2600_dbg(BES2600_DBG_TXRX, "%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); + + /* TODO:COMBO: __bes2600_sta_notify changed. */ + __bes2600_sta_notify(priv, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); +} + +static int bes2600_set_tim_impl(struct bes2600_vif *priv, bool aid0_bit_set) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + u16 tim_offset, tim_length; + + bes2600_dbg(BES2600_DBG_AP, "[AP] %s mcast: %s.\n", + __func__, aid0_bit_set ? "ena" : "dis"); + + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_length); + if (!skb) { + if (!__bes2600_flush(hw_priv, true, priv->if_id)) + wsm_unlock_tx(hw_priv); + return -ENOENT; + } + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. */ + skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + skb->data[tim_offset + 4] |= 1; + else + skb->data[tim_offset + 4] &= ~1; + } + + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); + + dev_kfree_skb(skb); + + return 0; +} + +void bes2600_set_tim_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, set_tim_work); + (void)bes2600_set_tim_impl(priv, priv->aid0_bit_set); +} + +int bes2600_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct bes2600_sta_priv *sta_priv = + (struct bes2600_sta_priv *)&sta->drv_priv; + struct bes2600_vif *priv = sta_priv->priv; + +#ifdef P2P_MULTIVIF + WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); +#endif + WARN_ON(priv->mode != NL80211_IFTYPE_AP); + queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); + return 0; +} + +void bes2600_set_cts_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, set_cts_work.work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + .ies = erp_ie, + .length = 3, + }; + u32 erp_info; + __le32 use_cts_prot; + down(&hw_priv->conf_lock); + erp_info = priv->erp_info; + up(&hw_priv->conf_lock); + use_cts_prot = + erp_info & WLAN_ERP_USE_PROTECTION ? + __cpu_to_le32(1) : 0; + + erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; + + bes2600_dbg(BES2600_DBG_AP, "[STA] ERP information 0x%x\n", erp_info); + + /* TODO:COMBO: If 2 interfaces are on the same channel they share + the same ERP values */ + WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot), + priv->if_id)); + /* If STA Mode update_ie is not required */ + if (priv->mode != NL80211_IFTYPE_STATION) { + WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); + } + + return; +} + +static int bes2600_set_btcoexinfo(struct bes2600_vif *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + WARN_ON(bes2600_upload_pspoll(priv)); + WARN_ON(bes2600_upload_null(priv)); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operationalRateSet & ~0xF) { + bes2600_dbg(BES2600_DBG_AP, "[STA] STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } else { + bes2600_dbg(BES2600_DBG_AP, "[STA] STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } + arg.nonErpInternalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + arg.nonErpInternalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } + + bes2600_dbg(BES2600_DBG_AP, "[STA] BTCOEX_INFO" + "MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", + priv->mode, + arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = WARN_ON(wsm_write_mib(cw12xx_vifpriv_to_hwpriv(priv), + WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg), priv->if_id)); + + return ret; +} + +void bes2600_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct ieee80211_conf *conf = &dev->conf; + const u8 override_fpsm_timeout = BES2600_FASTPS_IDLE_TIME; + +#ifdef P2P_MULTIVIF + if (priv->if_id == CW12XX_GENERIC_IF_ID) + return; +#endif + bes2600_info(BES2600_DBG_AP, "BSS CHANGED: %08x\n", changed); + down(&hw_priv->conf_lock); + if (changed & BSS_CHANGED_BSSID) { +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->tsm_info.sta_associated) { + unsigned now = jiffies; + hw_priv->tsm_info.sta_roamed = 1; + if ((now - hw_priv->tsm_info.txconf_timestamp_vo) > + (now - hw_priv->tsm_info.rx_timestamp_vo)) + hw_priv->tsm_info.use_rx_roaming = 1; + } else { + hw_priv->tsm_info.sta_associated = 1; + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + memcpy(priv->bssid, info->bssid, ETH_ALEN); + bes2600_setup_mac_pvif(priv); + } + + /* TODO: BSS_CHANGED_IBSS */ + if (changed & BSS_CHANGED_ARP_FILTER) { + + struct wsm_arp_ipv4_filter filter = {0}; + int i; + bes2600_dbg(BES2600_DBG_AP, "[STA] BSS_CHANGED_ARP_FILTER cnt: %d", + info->arp_addr_cnt); + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + if (info->arp_addr_cnt > 0) { + bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP); + } +#endif + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->arp_addr_cnt; i++) { + filter.ipv4Address[i] = info->arp_addr_list[i]; + bes2600_dbg(BES2600_DBG_AP, "[STA] addr[%d]: 0x%X\n", + i, filter.ipv4Address[i]); + } + if (vif->type == NL80211_IFTYPE_STATION) + filter.enable = (u32)BES2600_ENABLE_ARP_FILTER_OFFLOAD; + else if (priv->join_status == BES2600_JOIN_STATUS_AP) + filter.enable = (u32)(1<<1); + else + filter.enable = 0; + } else + filter.enable = 0; + + bes2600_dbg(BES2600_DBG_AP, "[STA] arp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + if (filter.enable) + bes2600_set_arpreply(dev, vif); + + priv->filter4 = filter; + + if (filter.enable && + (priv->join_status == BES2600_JOIN_STATUS_STA)) { + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fastPsmIdlePeriod = 0xFF; + else + priv->powersave_mode.fastPsmIdlePeriod = + conf->dynamic_ps_timeout << 1; + + /* override fast psm idle time */ + if (override_fpsm_timeout) { + priv->powersave_mode.fastPsmIdlePeriod = + override_fpsm_timeout << 1; + } + + if (priv->setbssparams_done) { + if (priv->user_power_set_true) + priv->powersave_mode.pmMode = priv->user_pm_mode; + else if (!priv->power_set_true) + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + } else + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + priv->power_set_true = 0; + priv->user_power_set_true = 0; + + if(!info->ps) { + bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE); + } + } + + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_GET_IP); + } + +#ifdef IPV6_FILTERING + if (changed & BSS_CHANGED_NDP_FILTER) { + struct wsm_ndp_ipv6_filter filter = {0}; + int i; + u16 *ipv6addr = NULL; + + bes2600_dbg(BES2600_DBG_AP, "[STA] BSS_CHANGED_NDP_FILTER " + "enabled: %d, cnt: %d\n", + info->ndp_filter_enabled, + info->ndp_addr_cnt); + + if (info->ndp_filter_enabled) { + if (vif->type == NL80211_IFTYPE_STATION) + filter.enable = (u32)BES2600_ENABLE_NDP_FILTER_OFFLOAD; + else if ((vif->type == NL80211_IFTYPE_AP)) + filter.enable = (u32)(1<<1); + else + filter.enable = 0; + } + + /* Currently only one IP address is supported by firmware. + * In case of more IPs ndp filtering will be disabled. */ + if (info->ndp_addr_cnt > 0 && + info->ndp_addr_cnt <= WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->ndp_addr_cnt; i++) { + priv->filter6.ipv6Address[i] = filter.ipv6Address[i] = info->ndp_addr_list[i]; + ipv6addr = (u16 *)(&filter.ipv6Address[i]); + bes2600_dbg(BES2600_DBG_AP, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \ + i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \ + cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \ + cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \ + cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7))); + } + } else { + filter.enable = 0; + for (i = 0; i < info->ndp_addr_cnt; i++) { + ipv6addr = (u16 *)(&info->ndp_addr_list[i]); + bes2600_dbg(BES2600_DBG_AP, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \ + i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \ + cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \ + cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \ + cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7))); + } + } + + bes2600_dbg(BES2600_DBG_AP, "[STA] ndp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + if (filter.enable) + bes2600_set_na(dev, vif); + + priv->filter6.enable = filter.enable; + + if (wsm_set_ndp_ipv6_filter(hw_priv, &filter, priv->if_id)) + WARN_ON(1); +#if 0 /*Commented out to disable Power Save in IPv6*/ + if (filter.enable && (priv->join_status == BES2600_JOIN_STATUS_STA) && (priv->vif->p2p) && + !(priv->firmware_ps_mode.pmMode & WSM_PSM_FAST_PS)) { + if(priv->setbssparams_done) { + struct wsm_set_pm pm = priv->powersave_mode; + int ret = 0; + + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + ret = bes2600_set_pm(priv, &priv->powersave_mode); + if(ret) { + priv->powersave_mode = pm; + } + } else { + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + } + } +#endif + } +#endif /*IPV6_FILTERING*/ + + if (changed & BSS_CHANGED_BEACON) { + bes2600_dbg(BES2600_DBG_AP, "BSS_CHANGED_BEACON\n"); +#ifdef HIDDEN_SSID + if (priv->join_status != BES2600_JOIN_STATUS_AP) + { + priv->hidden_ssid = info->hidden_ssid; + priv->ssid_length = info->ssid_len; + memcpy(priv->ssid, info->ssid, info->ssid_len); + } + else + bes2600_dbg(BES2600_DBG_AP, "priv->join_status=%d\n",priv->join_status); +#endif + WARN_ON(bes2600_upload_beacon(priv)); + WARN_ON(bes2600_update_beaconing(priv)); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + bes2600_dbg(BES2600_DBG_AP, "BSS_CHANGED_BEACON_ENABLED dummy\n"); + priv->enable_beacon = info->enable_beacon; + } + + if (changed & BSS_CHANGED_BEACON_INT) { + bes2600_dbg(BES2600_DBG_AP, "CHANGED_BEACON_INT\n"); + /* Restart AP only when connected */ + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(bes2600_update_beaconing(priv)); + } + + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(hw_priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(hw_priv); + + if (!info->assoc /* && !info->ibss_joined */) { + #ifdef P2P_STA_COEX + priv->cqm_link_loss_count = 400; + priv->cqm_beacon_loss_count = 200; + #else + priv->cqm_link_loss_count = 100; + priv->cqm_beacon_loss_count = 50; + #endif + priv->cqm_tx_failure_thold = 0; + } + priv->cqm_tx_failure_count = 0; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_ERP_PREAMBLE | + BSS_CHANGED_HT | + BSS_CHANGED_ERP_SLOT)) { + int is_combo = 0; + int i; + struct bes2600_vif *tmp_priv; + bes2600_info(BES2600_DBG_AP, "BSS_CHANGED_ASSOC assoc: %d.\n", info->assoc); + if (info->assoc) { /* TODO: ibss_joined */ + struct ieee80211_sta *sta = NULL; + if (info->dtim_period) + priv->join_dtim_period = info->dtim_period; + priv->beacon_int = info->beacon_int; + + /* Associated: kill join timeout */ + if(changed & BSS_CHANGED_ASSOC) { + cancel_delayed_work_sync(&priv->join_timeout); + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN); + bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_GET_IP); + txrx_opt_timer_init(priv->hw_priv); + } + + rcu_read_lock(); + if (info->bssid) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + /* TODO:COMBO:Change this once + * mac80211 changes are available */ + enum nl80211_channel_type ch_type; + BUG_ON(!hw_priv->channel); + hw_priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operationalRateSet = + __cpu_to_le32( + bes2600_rate_mask_to_wsm(hw_priv, + sta->supp_rates[ + hw_priv->channel->band])); + rcu_read_unlock(); + ch_type = cfg80211_get_chandef_type(&info->chandef); + bes2600_dbg(BES2600_DBG_AP, "[STA] ch %d, type: %d, HT oper mode: %d\n", + hw_priv->channel->hw_value, + ch_type, info->ht_operation_mode); + if ((changed & BSS_CHANGED_ASSOC) && + priv->join_status == BES2600_JOIN_STATUS_STA) { + struct wsm_switch_channel channel; + + hw_priv->ht_info.channel_type = ch_type; + channel.channelMode = hw_priv->ht_info.channel_type << 4; + channel.channelSwitchCount = 0; + channel.newChannelNumber = hw_priv->channel->hw_value; + wsm_switch_channel(hw_priv, &channel, priv->if_id); + bes2600_info(BES2600_DBG_AP, "channel type changed to %d !!!\n", hw_priv->ht_info.channel_type); + } + if (hw_priv->ht_info.operation_mode != info->ht_operation_mode) + hw_priv->ht_info.operation_mode = info->ht_operation_mode; + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + if (changed & BSS_CHANGED_ASSOC) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if (hw_priv->channel->band != NL80211_BAND_2GHZ) +#else + if (hw_priv->channel->band != IEEE80211_BAND_2GHZ) +#endif + bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTED_5G); + else + bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTED); + } +#endif + } else { + rcu_read_unlock(); + + memset(&hw_priv->ht_info, 0, sizeof(hw_priv->ht_info)); + priv->bss_params.operationalRateSet = -1; + } + //rcu_read_unlock(); + priv->htcap = (sta && bes2600_is_ht(&hw_priv->ht_info)); + bes2600_for_each_vif(hw_priv, tmp_priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv) +#else + if (!tmp_priv) +#endif + continue; + if (tmp_priv->join_status >= BES2600_JOIN_STATUS_STA) + is_combo++; + } + + if (is_combo > 1) { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE; + bes2600_info(BES2600_DBG_AP, "ASSOC is_combo %d\n",hw_priv->vif0_throttle); + } else if ((priv->join_status == BES2600_JOIN_STATUS_STA) && + priv->htcap) { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE; + bes2600_info(BES2600_DBG_AP, "ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle); + } else { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE; + bes2600_info(BES2600_DBG_AP, "ASSOC not_combo 11BG %d\n",hw_priv->vif0_throttle); + } + + if (sta) { + __le32 val = 0; + if (hw_priv->ht_info.operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) { + bes2600_info(BES2600_DBG_AP, "[STA]" + " Non-GF STA present\n"); + /* Non Green field capable STA */ + val = __cpu_to_le32(BIT(1)); + } + WARN_ON(wsm_write_mib(hw_priv, + WSM_MID_ID_SET_HT_PROTECTION, + &val, sizeof(val), priv->if_id)); + } + + priv->association_mode.greenfieldMode = + bes2600_ht_greenfield(&hw_priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + priv->association_mode.preambleType = + info->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG; + priv->association_mode.basicRateSet = __cpu_to_le32( + bes2600_rate_mask_to_wsm(hw_priv, + info->basic_rates)); + priv->association_mode.mpduStartSpacing = + bes2600_ht_ampdu_density(&hw_priv->ht_info); + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + priv->cqm_beacon_loss_count = + info->cqm_beacon_miss_thold; + priv->cqm_tx_failure_thold = + info->cqm_tx_fail_thold; + priv->cqm_tx_failure_count = 0; + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + + priv->bss_params.beaconLostCount = + priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : + priv->cqm_link_loss_count; + + priv->bss_params.aid = info->aid; + + if (priv->join_dtim_period < 1) + priv->join_dtim_period = 1; + + bes2600_dbg(BES2600_DBG_AP, "[STA] DTIM %d, interval: %d\n", + priv->join_dtim_period, priv->beacon_int); + bes2600_dbg(BES2600_DBG_AP, "[STA] Preamble: %d, " \ + "Greenfield: %d, Aid: %d, " \ + "Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preambleType, + priv->association_mode.greenfieldMode, + priv->bss_params.aid, + priv->bss_params.operationalRateSet, + priv->association_mode.basicRateSet); + WARN_ON(wsm_set_association_mode(hw_priv, + &priv->association_mode, priv->if_id)); + WARN_ON(wsm_keep_alive_period(hw_priv, + BES2600_KEEP_ALIVE_PERIOD /* sec */, + priv->if_id)); + WARN_ON(wsm_set_bss_params(hw_priv, &priv->bss_params, + priv->if_id)); + priv->setbssparams_done = true; + WARN_ON(wsm_set_beacon_wakeup_period(hw_priv, + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0, priv->if_id)); + if (priv->htcap) { + wsm_lock_tx(hw_priv); + /* Statically enabling block ack for TX/RX */ + WARN_ON(wsm_set_block_ack_policy(hw_priv, + hw_priv->ba_tid_mask, hw_priv->ba_tid_mask, + priv->if_id)); + wsm_unlock_tx(hw_priv); + } + + if (priv->vif->p2p) { + bes2600_dbg(BES2600_DBG_AP, + "[STA] Setting p2p powersave " + "configuration.\n"); + WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv, + &priv->p2p_ps_modeinfo, priv->if_id)); +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + bes2600_notify_noa(priv, BES2600_NOA_NOTIFICATION_DELAY); +#endif + } + + if (priv->mode == NL80211_IFTYPE_STATION) + WARN_ON(bes2600_upload_qosnull(priv)); + + if (hw_priv->is_BT_Present) + WARN_ON(bes2600_set_btcoexinfo(priv)); + + atomic_set(&priv->connect_in_process, 0); + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + bes2600_info(BES2600_DBG_AP, "queue delayed unjoin work for connect\n"); + if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } + +#if 0 + /* It's better to override internal TX rete; otherwise + * device sends RTS at too high rate. However device + * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a + * good choice for RTS/CTS, but that means PS poll + * will be sent at the same rate - impact on link + * budget. Not sure what is better.. */ + + /* Update: internal rate selection algorythm is not + * bad: if device is not receiving CTS at high rate, + * it drops RTS rate. + * So, conclusion: if-0 the code. Keep code just for + * information: + * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */ + + /* ~3 is a bug in device: RTS/CTS is not working at + * low rates */ + + __le32 internal_tx_rate = __cpu_to_le32(__ffs( + priv->association_mode.basicRateSet & ~3)); + WARN_ON(wsm_write_mib(priv, + WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &internal_tx_rate, + sizeof(internal_tx_rate))); +#endif + } else { +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + if (changed & BSS_CHANGED_ASSOC) + bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTED); +#endif + memset(&hw_priv->ht_info, 0, + sizeof(hw_priv->ht_info)); + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { + u32 prev_erp_info = priv->erp_info; + + if (priv->join_status == BES2600_JOIN_STATUS_AP) { + if (info->use_cts_prot) + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (prev_erp_info != priv->erp_info) + queue_delayed_work(hw_priv->workqueue, + &priv->set_cts_work, 0*HZ); + } + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? + __cpu_to_le32(9) : __cpu_to_le32(20); + bes2600_dbg(BES2600_DBG_AP, "[STA] Slot time :%d us.\n", + __le32_to_cpu(slot_time)); + WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_SLOT_TIME, + &slot_time, sizeof(slot_time), priv->if_id)); + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rollingAverageCount = 8, + }; + +#if 0 + /* For verification purposes */ + info->cqm_rssi_thold = -50; + info->cqm_rssi_hyst = 4; +#endif /* 0 */ + + bes2600_dbg(BES2600_DBG_AP, "[CQM] RSSI threshold " + "subscribe: %d +- %d\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + bes2600_dbg(BES2600_DBG_AP, "[CQM] Beacon loss subscribe: %d\n", + info->cqm_beacon_miss_thold); + bes2600_dbg(BES2600_DBG_AP, "[CQM] TX failure subscribe: %d\n", + info->cqm_tx_fail_thold); + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. */ + if (priv->cqm_use_rssi) { + threshold.upperThreshold = + info->cqm_rssi_thold + + info->cqm_rssi_hyst; + threshold.lowerThreshold = + info->cqm_rssi_thold; + } else { + /* convert RSSI to RCPI + * RCPI = (RSSI + 110) * 2 */ + threshold.upperThreshold = + (info->cqm_rssi_thold + + info->cqm_rssi_hyst + 110) * 2; + threshold.lowerThreshold = + (info->cqm_rssi_thold + 110) * 2; + } + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. */ + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + } + WARN_ON(wsm_set_rcpi_rssi_threshold(hw_priv, &threshold, + priv->if_id)); + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; + priv->cqm_tx_failure_count = 0; + + if (priv->cqm_beacon_loss_count != + info->cqm_beacon_miss_thold) { + priv->cqm_beacon_loss_count = + info->cqm_beacon_miss_thold; + priv->bss_params.beaconLostCount = + priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : + priv->cqm_link_loss_count; + /* Make sure we are associated before sending + * set_bss_params to firmware */ + if (priv->bss_params.aid) { + WARN_ON(wsm_set_bss_params(hw_priv, + &priv->bss_params, + priv->if_id)); + priv->setbssparams_done = true; + } + } +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + } + + if (changed & BSS_CHANGED_BANDWIDTH) { + enum nl80211_channel_type ch_type = cfg80211_get_chandef_type(&info->chandef); + + if (info->assoc && + hw_priv->ht_info.channel_type != ch_type && + priv->join_status == BES2600_JOIN_STATUS_STA) { + struct wsm_switch_channel channel; + + hw_priv->ht_info.channel_type = ch_type; + channel.channelMode = hw_priv->ht_info.channel_type << 4; + channel.channelSwitchCount = 0; + channel.newChannelNumber = hw_priv->channel->hw_value; + wsm_switch_channel(hw_priv, &channel, priv->if_id); + bes2600_info(BES2600_DBG_AP, "channel type changed to %d !!!\n", hw_priv->ht_info.channel_type); + } + } + + if (changed & BSS_CHANGED_PS) { + if (info->ps == false) + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + else if (conf->dynamic_ps_timeout <= 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + + /* set/clear ps active power busy event */ + if(priv->join_status == BES2600_JOIN_STATUS_STA) { + if(!info->ps) { + bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE); + } else { + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE); + } + } + + bes2600_info(BES2600_DBG_AP, "[STA] Aid: %d, Joined: %s, Powersave: %s\n", + priv->bss_params.aid, + priv->join_status == BES2600_JOIN_STATUS_STA ? "yes" : "no", + priv->powersave_mode.pmMode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : + priv->powersave_mode.pmMode == WSM_PSM_PS ? "WSM_PSM_PS" : + priv->powersave_mode.pmMode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : + "UNKNOWN"); + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fastPsmIdlePeriod = 0xFF; + else + priv->powersave_mode.fastPsmIdlePeriod = + conf->dynamic_ps_timeout << 1; + + /* override fast psm idle time */ + if (override_fpsm_timeout) { + priv->powersave_mode.fastPsmIdlePeriod = + override_fpsm_timeout << 1; + } + + if (priv->join_status == BES2600_JOIN_STATUS_STA && + priv->bss_params.aid && + priv->setbssparams_done && + priv->filter4.enable && + priv->powersave_mode.pmMode == WSM_PSM_ACTIVE) { + bes2600_set_pm(priv, &priv->powersave_mode); + } else { + priv->power_set_true = 1; + } + } + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + if (changed & BSS_CHANGED_P2P_PS) { + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + bes2600_dbg(BES2600_DBG_AP, "[AP] BSS_CHANGED_P2P_PS\n"); + bes2600_dbg(BES2600_DBG_AP, "[AP] Legacy PS: %d for AID %d " + "in %d mode.\n", info->p2p_ps.legacy_ps, + priv->bss_params.aid, priv->join_status); + + if (info->p2p_ps.legacy_ps >= 0) { + if (info->p2p_ps.legacy_ps > 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + + if(info->p2p_ps.ctwindow && info->p2p_ps.opp_ps) + priv->powersave_mode.pmMode = WSM_PSM_PS; + if (priv->join_status == BES2600_JOIN_STATUS_STA) + bes2600_set_pm(priv, &priv->powersave_mode); + } + + bes2600_dbg(BES2600_DBG_AP, "[AP] CTWindow: %d\n", + info->p2p_ps.ctwindow); + if (info->p2p_ps.ctwindow >= 128) + modeinfo->oppPsCTWindow = 127; + else if (info->p2p_ps.ctwindow >= 0) + modeinfo->oppPsCTWindow = info->p2p_ps.ctwindow; + + bes2600_dbg(BES2600_DBG_AP, "[AP] Opportunistic: %d\n", + info->p2p_ps.opp_ps); + switch (info->p2p_ps.opp_ps) { + case 0: + modeinfo->oppPsCTWindow &= ~(BIT(7)); + break; + case 1: + modeinfo->oppPsCTWindow |= BIT(7); + break; + default: + break; + } + + bes2600_dbg(BES2600_DBG_AP, "[AP] NOA: %d, %d, %d, %d\n", + info->p2p_ps.count, + info->p2p_ps.start, + info->p2p_ps.duration, + info->p2p_ps.interval); + /* Notice of Absence */ + modeinfo->count = info->p2p_ps.count; + + if (info->p2p_ps.count) { + /* In case P2P_GO we need some extra time to be sure + * we will update beacon/probe_resp IEs correctly */ +#define NOA_DELAY_START_MS 300 + if (priv->join_status == BES2600_JOIN_STATUS_AP) + modeinfo->startTime = + __cpu_to_le32(info->p2p_ps.start + + NOA_DELAY_START_MS); + else + modeinfo->startTime = + __cpu_to_le32(info->p2p_ps.start); + modeinfo->duration = + __cpu_to_le32(info->p2p_ps.duration); + modeinfo->interval = + __cpu_to_le32(info->p2p_ps.interval); + modeinfo->dtimCount = 1; + modeinfo->reserved = 0; + } else { + modeinfo->dtimCount = 0; + modeinfo->startTime = 0; + modeinfo->reserved = 0; + modeinfo->duration = 0; + modeinfo->interval = 0; + } + +#if defined(CONFIG_BES2600_STA_DEBUG) + print_hex_dump_bytes("p2p_set_ps_modeinfo: ", + DUMP_PREFIX_NONE, + (u8 *)modeinfo, + sizeof(*modeinfo)); +#endif /* CONFIG_BES2600_STA_DEBUG */ + if (priv->join_status == BES2600_JOIN_STATUS_STA || + priv->join_status == BES2600_JOIN_STATUS_AP) { + WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv, modeinfo, + priv->if_id)); + } + + /* Temporary solution while firmware don't support NOA change + * notification yet */ + bes2600_notify_noa(priv, 10); + } +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + + up(&hw_priv->conf_lock); +} + +void bes2600_multicast_start_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, multicast_start_work); + long tmo = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; + + cancel_work_sync(&priv->multicast_stop_work); + + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv->hw_priv); + bes2600_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, jiffies + tmo); + wsm_unlock_tx(priv->hw_priv); + } +} + +void bes2600_multicast_stop_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, multicast_stop_work); + + if (priv->aid0_bit_set) { + del_timer_sync(&priv->mcast_timeout); + wsm_lock_tx(priv->hw_priv); + priv->aid0_bit_set = false; + bes2600_set_tim_impl(priv, false); + wsm_unlock_tx(priv->hw_priv); + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) +void bes2600_mcast_timeout(struct timer_list *t) +{ + struct bes2600_vif *priv = from_timer(priv, t, mcast_timeout); + + wiphy_warn(priv->hw->wiphy, + "Multicast delivery timeout.\n"); + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + bes2600_bh_wakeup(cw12xx_vifpriv_to_hwpriv(priv)); + spin_unlock_bh(&priv->ps_state_lock); +} +#else +void bes2600_mcast_timeout(unsigned long arg) +{ + struct bes2600_vif *priv = (struct bes2600_vif *)arg; + + wiphy_warn(priv->hw->wiphy, + "Multicast delivery timeout.\n"); + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + bes2600_bh_wakeup(cw12xx_vifpriv_to_hwpriv(priv)); + spin_unlock_bh(&priv->ps_state_lock); +} +#endif + +int bes2600_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. + * In case of AMPDU aggregation in RX direction + * re-ordering of packets takes place on host. mac80211 + * needs the ADDBA Request to setup reodering.mac80211 also + * sends ADDBA Response which is discarded in the driver as + * FW generates the ADDBA Response on its own.*/ + int ret; + + switch (params->action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* Just return OK to mac80211 */ + ret = 0; + break; + default: + ret = -ENOTSUPP; + } + return ret; +} + +/* ******************************************************************** */ +/* WSM callback */ +void bes2600_suspend_resume(struct bes2600_vif *priv, + struct wsm_suspend_resume *arg) +{ + struct bes2600_common *hw_priv = + cw12xx_vifpriv_to_hwpriv(priv); + + bes2600_dbg(BES2600_DBG_AP, "[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); + + if (arg->multicast) { + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); + if (arg->stop) { + priv->tx_multicast = false; + } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than it could be saved. */ + u32 timeout = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; + timeout = jiffies_to_msecs(timeout); + bes2600_pwr_set_busy_event_with_timeout_async(hw_priv, + BES_PWR_LOCK_ON_MUL_REQ, timeout); + + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) { + cancel_tmo = true; + bes2600_bh_wakeup(hw_priv); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); + } else { + spin_lock_bh(&priv->ps_state_lock); + bes2600_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + bes2600_bh_wakeup(hw_priv); + } + return; +} + +/* ******************************************************************** */ +/* AP privates */ + +static int bes2600_upload_beacon(struct bes2600_vif *priv) +{ + int ret = 0; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + struct ieee80211_mgmt *mgmt; + u8 *erp_inf, *ies, *ht_info; + u32 ies_len; + + if (priv->vif->p2p || +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + hw_priv->channel->band == NL80211_BAND_5GHZ) +#else + hw_priv->channel->band == IEEE80211_BAND_5GHZ) +#endif + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + mgmt = (void *)frame.skb->data; + ies = mgmt->u.beacon.variable; + ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt); + + ht_info = (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len); + if (ht_info) { + /* Enable RIFS*/ + ht_info[3] |= 8; + } + + erp_inf = (u8 *)cfg80211_find_ie(WLAN_EID_ERP_INFO, ies, ies_len); + if (erp_inf) { + if (erp_inf[ERP_INFO_BYTE_OFFSET] + & WLAN_ERP_BARKER_PREAMBLE) + priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + if (erp_inf[ERP_INFO_BYTE_OFFSET] + & WLAN_ERP_NON_ERP_PRESENT) { + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + priv->erp_info |= WLAN_ERP_NON_ERP_PRESENT; + } else { + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + priv->erp_info &= ~WLAN_ERP_NON_ERP_PRESENT; + } + } + +#ifdef HIDDEN_SSID + if (priv->hidden_ssid) { + u8 *ssid_ie; + u8 ssid_len; + + bes2600_dbg(BES2600_DBG_AP, "%s: hidden_ssid set\n", __func__); + ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + WARN_ON(!ssid_ie); + ssid_len = ssid_ie[1]; + if (ssid_len) { + bes2600_dbg(BES2600_DBG_AP, "%s: hidden_ssid with zero content " + "ssid\n", __func__); + ssid_ie[1] = 0; + memmove(ssid_ie + 2, ssid_ie + 2 + ssid_len, + (ies + ies_len - + (ssid_ie + 2 + ssid_len))); + frame.skb->len -= ssid_len; + } else { + pbes2600_dbg(BES2600_DBG_AP, "%s: hidden ssid with ssid len 0" + " not supported", __func__); + dev_kfree_skb(frame.skb); + return -1; + } + } +#endif + + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + if (!ret) { +#ifdef PROBE_RESP_EXTRA_IE + ret = bes2600_upload_proberesp(priv); +#else + /* TODO: Distille probe resp; remove TIM + * and other beacon-specific IEs */ + *(__le16 *)frame.skb->data = + __cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + /* TODO: Ideally probe response template should separately + configured by supplicant through openmac. This is a + temporary work-around known to fail p2p group info + attribute related tests + */ + if (0 /* priv->vif->p2p */) + ret = wsm_set_probe_responder(priv, true); + else { + ret = wsm_set_template_frame(hw_priv, &frame, + priv->if_id); + WARN_ON(wsm_set_probe_responder(priv, false)); + } +#endif + } + dev_kfree_skb(frame.skb); + + return ret; +} + +#ifdef PROBE_RESP_EXTRA_IE +static int bes2600_upload_proberesp(struct bes2600_vif *priv) +{ + int ret = 0; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE, + }; +#ifdef HIDDEN_SSID + u8 *ssid_ie; +#endif + if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ) + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + +#ifdef HIDDEN_SSID + if (priv->hidden_ssid) { + int offset; + u8 ssid_len; + /* we are assuming beacon from upper layer will always contain + zero filled ssid for hidden ap. The beacon shall never have + ssid len = 0. + */ + + offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, + frame.skb->data + offset, + frame.skb->len - offset); + ssid_len = ssid_ie[1]; + if (ssid_len && (ssid_len == priv->ssid_length)) { + memcpy(ssid_ie + 2, priv->ssid, ssid_len); + } else { + bes2600_dbg(BES2600_DBG_AP, "%s: hidden ssid with mismatched ssid_len %d\n", + __func__, ssid_len); + dev_kfree_skb(frame.skb); + return -1; + } + } +#endif + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + WARN_ON(wsm_set_probe_responder(priv, false)); + + dev_kfree_skb(frame.skb); + + return ret; +} +#endif + +static int bes2600_upload_pspoll(struct bes2600_vif *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(cw12xx_vifpriv_to_hwpriv(priv), &frame, + priv->if_id); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int bes2600_upload_null(struct bes2600_vif *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); +#else + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); +#endif + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(cw12xx_vifpriv_to_hwpriv(priv), + &frame, priv->if_id); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int bes2600_upload_qosnull(struct bes2600_vif *priv) +{ + int ret = 0; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_QOS_NULL, + .rate = 0xFF, + }; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, true); +#else + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); +#endif + + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + + dev_kfree_skb(frame.skb); + + return ret; +} + +/* This API is nolonegr present in WSC */ +#if 0 +static int bes2600_enable_beaconing(struct bes2600_vif *priv, + bool enable) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_beacon_transmit transmit = { + .enableBeaconing = enable, + }; + + return wsm_beacon_transmit(hw_priv, &transmit, priv->if_id); +} +#endif + +static int start_dhcpd(void) +{ +#if 0 + volatile int result=0; + char cmdPath[]="/bin/bash"; + char cmdText[128]; + char* cmdArgv[]={cmdPath, "-c", cmdText, NULL}; + char* cmdArgv2[]={cmdPath, "-c", "/usr/sbin/dhcpd >> /tmp/log", NULL}; + char* cmdEnvp[]={"HOME=/", "PATH=/sbin:/bin:/usr/bin:/usr/sbin", NULL}; + static int ifname_id = 0; + + snprintf(cmdText, 128, "/sbin/ifconfig p2p-wlan0-%d 192.168.50.1/24 up >> /tmp/log2", ifname_id); + ifname_id++; + + result=call_usermodehelper(cmdPath,cmdArgv,cmdEnvp, UMH_WAIT_EXEC); + msleep(100); + result=call_usermodehelper(cmdPath,cmdArgv2,cmdEnvp, UMH_WAIT_EXEC); + //msleep(100); + //result=call_usermodehelper(cmdPath,cmdArgv3,cmdEnvp, UMH_WAIT_EXEC); +#else + volatile int result=0; + char cmdPath[]="/bin/bash"; + char* cmdArgv[]={cmdPath, "-c", "/usr/bin/start_dhcpd > /tmp/log", NULL}; + char* cmdEnvp[]={"HOME=/", "PATH=/sbin:/bin:/usr/bin:/usr/sbin", NULL}; + + result=call_usermodehelper(cmdPath,cmdArgv,cmdEnvp, UMH_WAIT_EXEC); +#endif + bes2600_info(BES2600_DBG_AP, "testDriver1 _init exec! The result of call_usermodehelper is %d\n",result); + bes2600_info(BES2600_DBG_AP, "testDriver1 _init exec! The process is \"%s\",pid is %d\n",current->comm,current->pid); + + return 0; +} + + +static int bes2600_start_ap(struct bes2600_vif *priv) +{ + int ret; +#ifndef HIDDEN_SSID + const u8 *ssidie; + struct sk_buff *skb; + int offset; +#endif + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_start start = { + .mode = priv->vif->p2p ? + WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, + /* TODO:COMBO:Change once mac80211 support is available */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + .band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ? +#else + .band = (hw_priv->channel->band == IEEE80211_BAND_5GHZ) ? +#endif + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = hw_priv->channel->hw_value, + .beaconInterval = conf->beacon_int, + .DTIMPeriod = conf->dtim_period, + .preambleType = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG, + .probeDelay = 100, + .basicRateSet = bes2600_rate_mask_to_wsm(hw_priv, + conf->basic_rates), +#ifdef P2P_MULTIVIF + .CTWindow = priv->if_id ? 0xFFFFFFFF : 0, /* it makes addr1's byte4 ^0x80 in firmware, + which matches with driver's addr2 */ +#endif + }; + + struct wsm_switch_channel channel = { + .channelMode = (cfg80211_get_chandef_type(&conf->chandef)) << 4, + .channelSwitchCount = 0, + .newChannelNumber = hw_priv->channel->hw_value, + }; + + struct wsm_inactivity inactivity = { + .min_inactivity = 9, + .max_inactivity = 1, + }; + + if (priv->vif->p2p) { + start_dhcpd(); + hw_priv->ht_info.channel_type = cfg80211_get_chandef_type(&conf->chandef); + } + + if (priv->if_id) + start.mode |= WSM_FLAG_MAC_INSTANCE_1; + else + start.mode &= ~WSM_FLAG_MAC_INSTANCE_1; + + hw_priv->connected_sta_cnt = 0; + +#ifndef HIDDEN_SSID + /* Get SSID */ + skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!skb)) + return -ENOMEM; + + offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); + + memset(priv->ssid, 0, sizeof(priv->ssid)); + if (ssidie) { + priv->ssid_length = ssidie[1]; + if (WARN_ON(priv->ssid_length > sizeof(priv->ssid))) + priv->ssid_length = sizeof(priv->ssid); + memcpy(priv->ssid, &ssidie[2], priv->ssid_length); + } else { + priv->ssid_length = 0; + } + dev_kfree_skb(skb); +#endif + + priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; + + start.ssidLength = priv->ssid_length; + memcpy(&start.ssid[0], priv->ssid, start.ssidLength); + + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + + bes2600_info(BES2600_DBG_AP, "[AP] ch: %d(%d), bcn: %d(%d), " + "brt: 0x%.8X, ssid: %.*s.\n", + start.channelNumber, start.band, + start.beaconInterval, start.DTIMPeriod, + start.basicRateSet, + start.ssidLength, start.ssid); + ret = WARN_ON(wsm_start(hw_priv, &start, priv->if_id)); + if (!ret) + ret = wsm_switch_channel(hw_priv, &channel, priv->if_id); + + if (!ret && priv->vif->p2p) { + bes2600_dbg(BES2600_DBG_AP, "[AP] Setting p2p powersave " + "configuration.\n"); + WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv, + &priv->p2p_ps_modeinfo, priv->if_id)); +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + bes2600_notify_noa(priv, BES2600_NOA_NOTIFICATION_DELAY); +#endif + } + + /*Set Inactivity time*/ + if (!(strstr(&start.ssid[0], "6.1.12"))) { + wsm_set_inactivity(hw_priv, &inactivity, priv->if_id); + } + if (!ret) { +#ifndef AP_AGGREGATE_FW_FIX + WARN_ON(wsm_set_block_ack_policy(hw_priv, + BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, priv->if_id)); +#else + if ((priv->if_id ==2) && !hw_priv->is_go_thru_go_neg) + WARN_ON(wsm_set_block_ack_policy(hw_priv, + BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + priv->if_id)); + else + WARN_ON(wsm_set_block_ack_policy(hw_priv, + BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + priv->if_id)); +#endif + priv->join_status = BES2600_JOIN_STATUS_AP; + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_AP); + /* bes2600_update_filtering(priv); */ + } + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE; + bes2600_info(BES2600_DBG_AP, "AP/GO mode BG THROTTLE %d\n", hw_priv->vif0_throttle); + return ret; +} + +static int bes2600_update_beaconing(struct bes2600_vif *priv) +{ + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + + if (priv->mode == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (priv->join_status != BES2600_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + bes2600_dbg(BES2600_DBG_AP, "ap restarting\n"); + wsm_lock_tx(hw_priv); + if (priv->join_status != BES2600_JOIN_STATUS_PASSIVE) + WARN_ON(wsm_reset(hw_priv, &reset, + priv->if_id)); + priv->join_status = BES2600_JOIN_STATUS_PASSIVE; + WARN_ON(bes2600_start_ap(priv)); + wsm_unlock_tx(hw_priv); + } else + bes2600_dbg(BES2600_DBG_AP, "ap started join_status: %d\n", + priv->join_status); + } + return 0; +} + +int bes2600_find_link_id(struct bes2600_vif *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->ps_state_lock); + + for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +int bes2600_alloc_link_id(struct bes2600_vif *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != BES2600_LINK_HARD && + !hw_priv->tx_queue_stats.link_map_cache[i + 1]) { + + unsigned long inactivity = + now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + struct bes2600_link_entry *entry = &priv->link_id_db[ret - 1]; + bes2600_dbg(BES2600_DBG_AP, "[AP] STA added, link_id: %d\n", + ret); + entry->status = BES2600_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, BES2600_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + } else { + wiphy_info(priv->hw->wiphy, + "[AP] Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +void bes2600_link_id_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, link_id_work); + struct bes2600_common *hw_priv = priv->hw_priv; + + wsm_flush_tx(hw_priv); + bes2600_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(hw_priv); +} + +void bes2600_link_id_gc_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, link_id_gc_work.work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i; + + if (priv->join_status != BES2600_JOIN_STATUS_AP) + return; + + wsm_lock_tx(hw_priv); + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == BES2600_LINK_RESERVE || + (priv->link_id_db[i].status == BES2600_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != BES2600_LINK_HARD) + priv->link_id_db[i].status = BES2600_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, + ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + if (need_reset) { + WARN_ON(cw12xx_unmap_link(priv, i + 1)); + } + map_link.link_id = i + 1; + WARN_ON(wsm_map_link(hw_priv, &map_link, priv->if_id)); + next_gc = min(next_gc, BES2600_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->ps_state_lock); + } else if (priv->link_id_db[i].status == BES2600_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + + BES2600_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = BES2600_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + memset(map_link.mac_addr, 0, ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + WARN_ON(cw12xx_unmap_link(priv, i + 1)); + spin_lock_bh(&priv->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + } else if (priv->link_id_db[i].status == BES2600_LINK_RESET || + priv->link_id_db[i].status == + BES2600_LINK_RESET_REMAP) { + int status = priv->link_id_db[i].status; + priv->link_id_db[i].status = BES2600_LINK_OFF; + priv->link_id_db[i].timestamp = now; + spin_unlock_bh(&priv->ps_state_lock); + WARN_ON(cw12xx_unmap_link(priv, i + 1)); + if (status == BES2600_LINK_RESET_REMAP) { + memcpy(map_link.mac_addr, + priv->link_id_db[i].mac, + ETH_ALEN); + map_link.link_id = i + 1; + WARN_ON(wsm_map_link(hw_priv, &map_link, + priv->if_id)); + next_gc = min(next_gc, + BES2600_LINK_ID_GC_TIMEOUT); + priv->link_id_db[i].status = + priv->link_id_db[i].prev_status; + } + spin_lock_bh(&priv->ps_state_lock); +#endif + } + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); + bes2600_dbg(BES2600_DBG_AP, "[AP] STA removed, link_id: %d\n", + i + 1); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (next_gc != -1) + queue_delayed_work(hw_priv->workqueue, + &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(hw_priv); +} + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) +void bes2600_notify_noa(struct bes2600_vif *priv, int delay) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct cfg80211_p2p_ps p2p_ps = {0}; + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + + bes2600_dbg(BES2600_DBG_AP, "[AP]: %s called\n", __func__); + + if (priv->join_status != BES2600_JOIN_STATUS_AP) + return; + + if (delay) + msleep(delay); + + if (!WARN_ON(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo))) { +#if defined(CONFIG_BES2600_STA_DEBUG) + print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ", + DUMP_PREFIX_NONE, + (u8 *)modeinfo, + sizeof(*modeinfo)); +#endif /* CONFIG_BES2600_STA_DEBUG */ + p2p_ps.opp_ps = !!(modeinfo->oppPsCTWindow & BIT(7)); + p2p_ps.ctwindow = modeinfo->oppPsCTWindow & (~BIT(7)); + p2p_ps.count = modeinfo->count; + p2p_ps.start = __le32_to_cpu(modeinfo->startTime); + p2p_ps.duration = __le32_to_cpu(modeinfo->duration); + p2p_ps.interval = __le32_to_cpu(modeinfo->interval); + p2p_ps.index = modeinfo->reserved; + + ieee80211_p2p_noa_notify(priv->vif, + &p2p_ps, + GFP_KERNEL); + } +} +#endif + +int cw12xx_unmap_link(struct bes2600_vif *priv, int link_id) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + int ret = 0; + + if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) { + struct wsm_map_link maplink = { + .link_id = link_id, + .unmap = true, + }; + if (link_id) + memcpy(&maplink.mac_addr[0], + priv->link_id_db[link_id - 1].mac, ETH_ALEN); + return wsm_map_link(hw_priv, &maplink, priv->if_id); + } else { + struct wsm_reset reset = { + .link_id = link_id, + .reset_statistics = true, + }; + ret = wsm_reset(hw_priv, &reset, priv->if_id); + return ret; + } +} +#ifdef AP_HT_CAP_UPDATE +void bes2600_ht_info_update_work(struct work_struct *work) +{ + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u8 *ht_info, *ies; + u32 ies_len; + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, ht_info_update_work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + + skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!skb)) + return; + + mgmt = (void *)skb->data; + ies = mgmt->u.beacon.variable; + ies_len = skb->len - (u32)(ies - (u8 *)mgmt); + ht_info= (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len); + if(ht_info && priv->ht_info == HT_INFO_MASK) { + + ht_info[HT_INFO_OFFSET] |= 0x11; + update_ie.ies = ht_info; + update_ie.length = HT_INFO_IE_LEN; + + WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); + + + } + dev_kfree_skb(skb); +} +#endif + diff --git a/drivers/staging/bes2600/ap.h b/drivers/staging/bes2600/ap.h new file mode 100644 index 000000000000..f9d85d847397 --- /dev/null +++ b/drivers/staging/bes2600/ap.h @@ -0,0 +1,58 @@ +/* + * mac80211 STA and AP API for mac80211 BES2600 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#ifndef AP_H_INCLUDED +#define AP_H_INCLUDED + +#define BES2600_NOA_NOTIFICATION_DELAY 10 + +int bes2600_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int bes2600_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void bes2600_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void bes2600_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); + +int bes2600_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params); + +void bes2600_suspend_resume(struct bes2600_vif *priv, + struct wsm_suspend_resume *arg); +void bes2600_set_tim_work(struct work_struct *work); +void bes2600_set_cts_work(struct work_struct *work); +void bes2600_multicast_start_work(struct work_struct *work); +void bes2600_multicast_stop_work(struct work_struct *work); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) +void bes2600_mcast_timeout(struct timer_list *t); +#else +void bes2600_mcast_timeout(unsigned long arg); +#endif +int bes2600_find_link_id(struct bes2600_vif *priv, const u8 *mac); +int bes2600_alloc_link_id(struct bes2600_vif *priv, const u8 *mac); +void bes2600_link_id_work(struct work_struct *work); +void bes2600_link_id_gc_work(struct work_struct *work); +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) +void bes2600_notify_noa(struct bes2600_vif *priv, int delay); +#endif +int cw12xx_unmap_link(struct bes2600_vif *priv, int link_id); +#ifdef AP_HT_CAP_UPDATE +void bes2600_ht_info_update_work(struct work_struct *work); +#endif + +#endif diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h new file mode 100644 index 000000000000..ecfe154a0283 --- /dev/null +++ b/drivers/staging/bes2600/bes2600.h @@ -0,0 +1,850 @@ +/* + * Common private data for BES2600 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_H +#define BES2600_H + +#include +#include +#include +#include +#include +#include +#include +#ifdef P2P_MULTIVIF +#define CW12XX_MAX_VIFS (3) +#else +#define CW12XX_MAX_VIFS (2) +#endif +#define CW12XX_GENERIC_IF_ID (2) +#define CW12XX_HOST_VIF0_11N_THROTTLE (63) +#define CW12XX_HOST_VIF1_11N_THROTTLE (63) +#define CW12XX_HOST_VIF0_11BG_THROTTLE (15) +#define CW12XX_HOST_VIF1_11BG_THROTTLE (15) +#if 0 +#define CW12XX_FW_VIF0_THROTTLE (15) +#define CW12XX_FW_VIF1_THROTTLE (15) +#endif +#define CW12XX_MAX_QUEUE_SZ (128) + +#define IEEE80211_FCTL_WEP 0x4000 +#define IEEE80211_QOS_DATAGRP 0x0080 +#define WSM_KEY_MAX_IDX 20 + +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "ht.h" +#include "pm.h" +#include "fwio.h" +#include "bes2600_log.h" +#include "bes_pwr.h" +#include "tx_loop.h" +#ifdef CONFIG_BES2600_TESTMODE +#include "bes_nl80211_testmode_msg.h" +#endif /*CONFIG_BES2600_TESTMODE*/ + + +/* extern */ struct sbus_ops; +/* extern */ struct task_struct; +/* extern */ struct bes2600_debug_priv; +/* extern */ struct bes2600_debug_common; +/* extern */ struct firmware; + +/* #define ROC_DEBUG */ + +/* hidden ssid is only supported when separate probe resp IE + configuration is supported */ +#ifdef PROBE_RESP_EXTRA_IE +#define HIDDEN_SSID 1 +#endif + +#if defined(CONFIG_BES2600_TXRX_DEBUG) +#define txrx_printk(...) printk(__VA_ARGS__) +#else +#define txrx_printk(...) +#endif + +#define BES2600_MAX_CTRL_FRAME_LEN (0x1000) + +#define CW1250_MAX_STA_IN_AP_MODE (14) +#define WLAN_LINK_ID_MAX (CW1250_MAX_STA_IN_AP_MODE + 3) + +#define BES2600_MAX_STA_IN_AP_MODE (5) +#define BES2600_MAX_REQUEUE_ATTEMPTS (5) +#define BES2600_LINK_ID_UNMAPPED (15) + +#define BES2600_MAX_TID (8) + +#define BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F) +#define BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F) +#define BES2600_RX_BLOCK_ACK_ENABLED_FOR_BE_TID \ + (BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID & 0x01) +#define BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0) +#define BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0) + +#define BES2600_BLOCK_ACK_CNT (30) +#define BES2600_BLOCK_ACK_THLD (800) +#define BES2600_BLOCK_ACK_HIST (3) +#define BES2600_BLOCK_ACK_INTERVAL (1 * HZ / BES2600_BLOCK_ACK_HIST) +#define CW12XX_ALL_IFS (-1) +#ifdef ROAM_OFFLOAD +#define BES2600_SCAN_TYPE_ACTIVE 0x1000 +#define BES2600_SCAN_BAND_5G 0X2000 +#endif /*ROAM_OFFLOAD*/ + +#define IEEE80211_FCTL_WEP 0x4000 +#define IEEE80211_QOS_DATAGRP 0x0080 +#ifdef CONFIG_BES2600_TESTMODE +#define BES2600_SCAN_MEASUREMENT_PASSIVE (0) +#define BES2600_SCAN_MEASUREMENT_ACTIVE (1) +#endif + +#ifdef MCAST_FWDING +#define WSM_MAX_BUF 30 +#endif + +#ifdef BSS_LOSS_CHECK +#define BSS_LOSS_CK_THR 1 +#define BSS_LOSS_CK_INV 2000 +#define BSS_LOSS_CFM_THR 1 +#define BSS_LOSS_CFM_INV 200 +#else +#define BSS_LOSS_CFM_INV 0 +#endif + +/* Please keep order */ +enum bes2600_join_status { + BES2600_JOIN_STATUS_PASSIVE = 0, + BES2600_JOIN_STATUS_MONITOR, + BES2600_JOIN_STATUS_STA, + BES2600_JOIN_STATUS_AP, +}; + +enum bes2600_link_status { + BES2600_LINK_OFF, + BES2600_LINK_RESERVE, + BES2600_LINK_SOFT, + BES2600_LINK_HARD, +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + BES2600_LINK_RESET, + BES2600_LINK_RESET_REMAP, +#endif +}; + +enum bes2600_bss_loss_status { + BES2600_BSS_LOSS_NONE, + BES2600_BSS_LOSS_CHECKING, + BES2600_BSS_LOSS_CONFIRMING, + BES2600_BSS_LOSS_CONFIRMED, +}; + +struct bes2600_link_entry { + unsigned long timestamp; + enum bes2600_link_status status; +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + enum bes2600_link_status prev_status; +#endif + u8 mac[ETH_ALEN]; + u8 buffered[BES2600_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +#if defined(ROAM_OFFLOAD) || defined(CONFIG_BES2600_TESTMODE) +struct bes2600_testframe { + u8 len; + u8 *data; +}; +#endif +#ifdef CONFIG_BES2600_TESTMODE +struct advance_scan_elems { + u8 scanMode; + u16 duration; +}; +/** + * bes2600_tsm_info - Keeps information about ongoing TSM collection + * @ac: Access category for which metrics to be collected + * @use_rx_roaming: Use received voice packets to compute roam delay + * @sta_associated: Set to 1 after association + * @sta_roamed: Set to 1 after successful roaming + * @roam_delay: Roam delay + * @rx_timestamp_vo: Timestamp of received voice packet + * @txconf_timestamp_vo: Timestamp of received tx confirmation for + * successfully transmitted VO packet + * @sum_pkt_q_delay: Sum of packet queue delay + * @sum_media_delay: Sum of media delay + * + */ +struct bes2600_tsm_info { + u8 ac; + u8 use_rx_roaming; + u8 sta_associated; + u8 sta_roamed; + u16 roam_delay; + u32 rx_timestamp_vo; + u32 txconf_timestamp_vo; + u32 sum_pkt_q_delay; + u32 sum_media_delay; +}; + +/** + * bes2600_start_stop_tsm - To start or stop collecting TSM metrics in + * bes2600 driver + * @start: To start or stop collecting TSM metrics + * @up: up for which metrics to be collected + * @packetization_delay: Packetization delay for this TID + * + */ +struct bes2600_start_stop_tsm { + u8 start; /*1: To start, 0: To stop*/ + u8 up; + u16 packetization_delay; +}; + +#endif /* CONFIG_BES2600_TESTMODE */ + +/* + * tcp & udp alive + */ +#ifdef CONFIG_BES2600_KEEP_ALIVE +#define IP_KEEPALIVE_MAX_LEN (256 + 8) +#define AES_KEY_IV_LEN (16 + 16) +#define AES_KEY_LEN (16) +#define AES_IV_LEN (16) +#define NUM_IP_FRAMES 8 +#define TCP_PROTO 6 +#define UDP_PROTO 17 + +#define KLV_VENDOR_DEFAULT 0 +#define KLV_VENDOR_XM 1 +#define WEBSOCKET_HD_LEN 6 + +#ifdef P2P_MULTIVIF +#define NET_DEVICE_NUM (3) +#else +#define NET_DEVICE_NUM (2) +#endif + +struct ip_header { + /* version / header length */ + uint8_t _v_hl; + /* type of service */ + uint8_t _tos; + /* total length */ + uint16_t _len; + /* identification */ + uint16_t _id; + /* fragment offset field */ + uint16_t _offset; + /* time to live */ + uint8_t _ttl; + /* protocol*/ + uint8_t _proto; + /* checksum */ + uint16_t _chksum; + /* source and destination IP addresses */ + uint32_t src; + uint32_t dest; +} ; + +struct tcp_header { + uint16_t src; + uint16_t dest; + uint32_t seqno; + uint32_t ackno; + uint16_t _hdrlen_rsvd_flags; + uint16_t wnd; + uint16_t chksum; + uint16_t urgp; +}; + +struct udp_header { + uint16_t src; + uint16_t dest; + uint16_t len; + uint16_t chksum; +}; + +struct ip_alive_info { + uint8_t idx_used; + uint8_t proto; /* 0 for udp and 1 for tcp; */ + uint16_t src_port; + uint16_t dest_port; + uint32_t src_ip; + uint32_t dest_ip; + uint16_t len; + uint32_t next_seqno; + uint8_t payload[IP_KEEPALIVE_MAX_LEN]; + uint8_t dest_mac[6]; +}; + +struct ip_alive_cfg { + struct ip_header iphd; + struct tcp_header tcphd; + struct udp_header udphd; + struct ip_alive_info bd; + uint8_t aes_key[AES_KEY_LEN]; + uint8_t aes_iv[AES_IV_LEN]; + uint8_t klv_vendor; /* stands for different vendor's keep-alive resolution; */ +}; +#endif /* CONFIG_BES2600_KEEP_ALIVE */ + +struct bes2600_common { + struct bes2600_debug_common *debug; + struct bes2600_queue tx_queue[4]; + struct bes2600_queue_stats tx_queue_stats; + + struct ieee80211_hw *hw; + struct mac_address addresses[CW12XX_MAX_VIFS]; + + /*Will be a pointer to a list of VIFs - Dynamically allocated */ + struct ieee80211_vif *vif_list[CW12XX_MAX_VIFS]; + atomic_t num_vifs; + atomic_t netdevice_start; + spinlock_t vif_list_lock; + u32 if_id_slot; + struct device *pdev; + struct workqueue_struct *workqueue; + + struct semaphore conf_lock; + + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; + + /* HW/FW type (HIF_...) */ + int hw_type; + int hw_revision; + int fw_revision; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + int noise; + + /* calibration, output power limit and rssi<->dBm conversation data */ + + /* BBP/MAC state */ + const struct firmware *sdd; + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; + u8 mac_addr[ETH_ALEN]; + /*TODO:COMBO: To be made per VIFF after mac80211 support */ + struct ieee80211_channel *channel; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + /* TODO:COMBO: According to Hong aggregation will happen per VIFF. + * Keeping in common structure for the time being. Will be moved to VIFF + * after the mechanism is clear */ + u8 ba_tid_mask; + int ba_acc; /*TODO: Same as above */ + int ba_cnt; /*TODO: Same as above */ + int ba_cnt_rx; /*TODO: Same as above */ + int ba_acc_rx; /*TODO: Same as above */ + int ba_hist; /*TODO: Same as above */ + struct timer_list ba_timer;/*TODO: Same as above */ + spinlock_t ba_lock; /*TODO: Same as above */ + bool ba_ena; /*TODO: Same as above */ + struct work_struct ba_work; /*TODO: Same as above */ + bool is_BT_Present; + bool is_go_thru_go_neg; + u8 conf_listen_interval; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + atomic_t bh_suspend; + + struct workqueue_struct *bh_workqueue; + struct work_struct bh_work; + + int bh_error; + wait_queue_head_t bh_wq; + wait_queue_head_t bh_evt_wq; + int buf_id_tx; /* byte */ + int buf_id_rx; /* byte */ + int wsm_rx_seq[2]; /* idx 0: Normal tx/rx, idx 1: special cmd */ + int wsm_tx_seq[2]; /* idx 0: Normal tx/rx, idx 1: special cmd */ + int wsm_tx_pending[2]; /* idx 0: Normal tx/rx, idx 1: special cmd */ + struct timer_list mcu_mon_timer; + struct timer_list lmac_mon_timer; + int hw_bufs_used; + int hw_bufs_used_vif[CW12XX_MAX_VIFS]; + struct sk_buff *skb_cache; + /* Keep bes2600 awake (WUP = 1) 1 second after each scan to avoid + * FW issue with sleeping/waking up. */ + atomic_t recent_scan; + + /* WSM */ + struct wsm_caps wsm_caps; + struct semaphore wsm_cmd_sema; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + struct wsm_cbc wsm_cbc; + atomic_t tx_lock; + u32 pending_frame_id; +#ifdef CONFIG_BES2600_TESTMODE + /* Device Power Range */ + struct wsm_tx_power_range txPowerRange[2]; + /* Advance Scan */ + struct advance_scan_elems advanceScanElems; + bool enable_advance_scan; + struct delayed_work advance_scan_timeout; +#endif /* CONFIG_BES2600_TESTMODE */ + + /* WSM debug */ + int wsm_enable_wsm_dumps; + u32 wsm_dump_max_size; + + /* Scan status */ + struct bes2600_scan scan; + + /* TX/RX */ + unsigned long rx_timestamp; + + /* Scan Timestamp */ + unsigned long scan_timestamp; + + /* WSM events */ + spinlock_t event_queue_lock; + struct list_head event_queue; + struct work_struct event_handler; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + /* cryptographic engine information */ + + /* bit field of glowing LEDs */ + u16 softled_state; + + /* statistics */ + struct ieee80211_low_level_stats stats; + + struct bes2600_ht_info ht_info; + int tx_burst_idx; + + struct ieee80211_iface_limit if_limits1[2]; + struct ieee80211_iface_limit if_limits2[2]; + struct ieee80211_iface_limit if_limits3[2]; + struct ieee80211_iface_combination if_combs[3]; + + struct semaphore wsm_oper_lock; + struct delayed_work rem_chan_timeout; + MIB_TXRX_OPT_PARAM txrx_opt_param; + u32 rtsvalue; + spinlock_t rtsvalue_lock; + struct timer_list txrx_opt_timer; + struct work_struct dynamic_opt_txrx_work; + atomic_t remain_on_channel; + int roc_if_id; + u64 roc_cookie; + wait_queue_head_t offchannel_wq; + u16 offchannel_done; + u16 prev_channel; + int if_id_selected; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; +#ifdef MCAST_FWDING + struct wsm_buf wsm_release_buf[WSM_MAX_BUF]; + u8 buf_released; +#endif +#ifdef ROAM_OFFLOAD + u8 auto_scanning; + u8 frame_rcvd; + u8 num_scanchannels; + u8 num_2g_channels; + u8 num_5g_channels; + struct wsm_scan_ch scan_channels[48]; + struct sk_buff *beacon; + struct sk_buff *beacon_bkp; + struct bes2600_testframe testframe; +#endif /*ROAM_OFFLOAD*/ +#ifdef CONFIG_BES2600_TESTMODE + struct bes2600_testframe test_frame; + struct bes_tsm_stats tsm_stats; + struct bes2600_tsm_info tsm_info; + spinlock_t tsm_lock; + struct bes2600_start_stop_tsm start_stop_tsm; +#endif /* CONFIG_BES2600_TESTMODE */ + u8 connected_sta_cnt; + u16 vif0_throttle; + u16 vif1_throttle; + int scan_switch_if_id; +#ifdef CONFIG_BES2600_WAPI_SUPPORT + int last_ins_wapi_usk_id; + int last_del_wapi_usk_id; +#endif +#ifdef CONFIG_BES2600_TESTMODE + struct semaphore vendor_rf_cmd_replay_sema; +#endif + + /* member for coexistence */ + struct work_struct coex_work; + struct list_head coex_event_list; + spinlock_t coex_event_lock; + + /* member for low power */ + struct bes2600_pwr_t bes_power; + + /* member for tx loop */ + struct bes2600_tx_loop tx_loop; + +#ifdef CONFIG_BES2600_KEEP_ALIVE + struct ip_alive_cfg iac[NUM_IP_FRAMES]; +#endif +}; + +/* Virtual Interface State. One copy per VIF */ +struct bes2600_vif { + atomic_t enabled; + spinlock_t vif_lock; + int if_id; + /*TODO: Split into Common and VIF parts */ + struct bes2600_debug_priv *debug; + /* BBP/MAC state */ + u8 bssid[ETH_ALEN]; + struct wsm_edca_params edca; + struct wsm_tx_queue_params tx_queue_params; + struct wsm_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct wsm_set_pm powersave_mode; + struct wsm_set_pm firmware_ps_mode; + int power_set_true; + int user_power_set_true; + u8 user_pm_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + unsigned cqm_tx_failure_thold; + unsigned cqm_tx_failure_count; + unsigned cmq_tx_success_count; + bool cqm_use_rssi; + int cqm_link_loss_count; + int cqm_beacon_loss_count; + int mode; + bool enable_beacon; + int beacon_int; + size_t ssid_length; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +#ifdef HIDDEN_SSID + bool hidden_ssid; +#endif + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_table bf_table; + struct wsm_beacon_filter_control bf_control; + struct wsm_multicast_filter multicast_filter; + bool has_multicast_subscription; + struct wsm_broadcast_addr_filter broadcast_filter; + bool disable_beacon_filter; + struct wsm_arp_ipv4_filter filter4; +#ifdef IPV6_FILTERING + struct wsm_ndp_ipv6_filter filter6; +#endif /*IPV6_FILTERING*/ + struct work_struct update_filtering_work; + struct work_struct set_beacon_wakeup_period_work; + struct bes2600_pm_state_vif pm_state_vif; + /*TODO: Add support in mac80211 for psmode info per VIF */ + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + u32 listen_interval; + u32 erp_info; + bool powersave_enabled; + + /* WSM Join */ + enum bes2600_join_status join_status; + u8 join_bssid[ETH_ALEN]; + struct work_struct join_work; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + struct work_struct offchannel_work; + int join_dtim_period; + bool delayed_unjoin; + atomic_t connect_in_process; + + /* Security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + unsigned long rx_timestamp; + u32 cipherType; + + + /* AP powersave */ + u32 link_id_map; + u32 max_sta_ap_mode; + u32 link_id_after_dtim; + u32 link_id_uapsd; + u32 link_id_max; + u32 wsm_key_max_idx; + struct bes2600_link_entry link_id_db[CW1250_MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + bool aid0_bit_set; + spinlock_t ps_state_lock; + bool buffered_multicasts; + bool tx_multicast; + struct work_struct set_tim_work; + struct delayed_work set_cts_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; + + /* CQM Implementation */ + struct delayed_work bss_loss_work; + struct delayed_work connection_loss_work; + struct work_struct tx_failure_work; + int delayed_link_loss; + spinlock_t bss_loss_lock; + int bss_loss_status; + int bss_loss_confirm_id; + + struct ieee80211_vif *vif; + struct bes2600_common *hw_priv; + struct ieee80211_hw *hw; + + /* ROC implementation */ + struct delayed_work pending_offchanneltx_work; +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + /* Workaround for WFD testcase 6.1.10*/ + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; +#endif + bool htcap; +#ifdef AP_HT_CAP_UPDATE + u16 ht_info; + struct work_struct ht_info_update_work; +#endif + bool pmf; + + u32 hw_value; + /* dot11CountersTable */ + u32 dot11TransmittedFragmentCount; + u32 dot11MulticastTransmittedFrameCount; + u32 dot11FailedCount; + u32 dot11RetryCount; + u32 dot11MultipleRetryCount; + u32 dot11FrameDuplicateCount; + u32 dot11ReceivedFragmentCount; + u32 dot11RxReorderLeakCount; + u32 dot11ReceivedBytes; + u32 dot11ReceivedDataBytes; + u32 dot11MulticastReceivedFrameCount; + u32 dot11TransmittedFrameCount; + u32 dot11TransmittedBytes; + u32 dot11TransmittedDataBytes; + u32 dot11Txbps; + u32 dot11Rxbps; +}; +struct bes2600_sta_priv { + int link_id; + struct bes2600_vif *priv; +}; +enum bes2600_data_filterid { + IPV4ADDR_FILTER_ID = 0, +#ifdef IPV6_FILTERING + IPV6ADDR_FILTER_ID, +#endif /*IPV6_FILTERING*/ +}; + +static inline +struct bes2600_common *cw12xx_vifpriv_to_hwpriv(struct bes2600_vif *priv) +{ + return priv->hw_priv; +} + + +static inline +struct bes2600_vif *cw12xx_get_vif_from_ieee80211(struct ieee80211_vif *vif) +{ + return (struct bes2600_vif *)vif->drv_priv; +} + +static inline +struct bes2600_vif *cw12xx_hwpriv_to_vifpriv(struct bes2600_common *hw_priv, + int if_id) +{ + struct bes2600_vif *vif; + + if (WARN_ON((-1 == if_id) || (if_id > CW12XX_MAX_VIFS))) + return NULL; + /* TODO:COMBO: During scanning frames can be received + * on interface ID 3 */ + spin_lock(&hw_priv->vif_list_lock); + if (!hw_priv->vif_list[if_id]) { + spin_unlock(&hw_priv->vif_list_lock); + return NULL; + } + + vif = cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); + WARN_ON(!vif); + if (vif) + spin_lock(&vif->vif_lock); + spin_unlock(&hw_priv->vif_list_lock); + return vif; +} + +static inline +struct bes2600_vif *__cw12xx_hwpriv_to_vifpriv(struct bes2600_common *hw_priv, + int if_id) +{ + WARN_ON((-1 == if_id) || (if_id > CW12XX_MAX_VIFS)); + /* TODO:COMBO: During scanning frames can be received + * on interface ID 3 */ + if (!hw_priv->vif_list[if_id]) { + return NULL; + } + + return cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); +} + +static inline +struct bes2600_vif *cw12xx_get_activevif(struct bes2600_common *hw_priv) +{ + return cw12xx_hwpriv_to_vifpriv(hw_priv, ffs(hw_priv->if_id_slot)-1); +} + +static inline bool is_hardware_cw1250(struct bes2600_common *hw_priv) +{ + return (hw_priv->hw_revision == BES2600_HW_REV_CUT20); +} + +static inline bool is_hardware_cw1260(struct bes2600_common *hw_priv) +{ + return (hw_priv->hw_revision == BES2600_HW_REV_CUT10); +} + +static inline int cw12xx_get_nr_hw_ifaces(struct bes2600_common *hw_priv) +{ + switch(hw_priv->hw_revision) { + case BES2600_HW_REV_CUT10: + case BES2600_HW_REV_CUT11: + case BES2600_HW_REV_CUT20: + case BES2600_HW_REV_CUT22: + return 1; + case CW1250_HW_REV_CUT10: + return 3; + default: + return 1; + } +} + +#ifdef CONFIG_BES2600_KEEP_ALIVE +/* IPV4 host addr info */ +struct ipv4_addr_info { + u8 filter_mode; + u8 address_mode; + u8 ipv4[4]; +}; + +/* tcp keep alive test period */ +struct MIB_TCP_KEEP_ALIVE_PERIOD { + u16 TcpKeepAlivePeriod; /* in seconds */ + u8 EncrType; /* (ex. WSM_KEY_TYPE_WEP_DEFAULT) */ + u8 Reserved; +}; + +#ifdef VENDOR_XM_KEEPALIVE +struct ip_alive_satus { + bool udp; + bool tcp; +}; + +void bes2600_get_keepalive_info(struct bes2600_common *hw_priv, struct ip_alive_satus *status); +#endif + +int bes2600_set_ip_offload(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + struct ip_alive_cfg *tac, + u16 idx); + +int bes2600_del_ip_offload(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + u8 stream_idx); + +int bes2600_en_ip_offload(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + u16 period_in_s); +int bes2600_set_ipv4addrfilter(struct bes2600_common *hw_priv, u8 *data, int if_id); +#endif /* CONFIG_BES2600_KEEP_ALIVE */ + +#ifdef IPV6_FILTERING +/* IPV6 host addr info */ +struct ipv6_addr_info { + u8 filter_mode; + u8 address_mode; + u16 ipv6[8]; +}; +#endif /*IPV6_FILTERING*/ + +/* interfaces for the drivers */ +int bes2600_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct bes2600_common **pself); +void bes2600_core_release(struct bes2600_common *self); + +static inline void bes2600_tx_queues_lock(struct bes2600_common *hw_priv) +{ + int i; + for (i = 0; i < 4; ++i) + bes2600_queue_lock(&hw_priv->tx_queue[i]); +} + +static inline void bes2600_tx_queues_unlock(struct bes2600_common *hw_priv) +{ + int i; + for (i = 0; i < 4; ++i) + bes2600_queue_unlock(&hw_priv->tx_queue[i]); +} + +/* Datastructure for LLC-SNAP HDR */ +#define P80211_OUI_LEN 3 + +struct ieee80211_snap_hdr { + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ +} __packed; + +#define bes2600_for_each_vif(_hw_priv, _priv, _i) \ +for ( \ + _i = 0; \ + (_i < CW12XX_MAX_VIFS) && \ + (_priv = hw_priv->vif_list[_i] ? \ + cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[_i]) : NULL); \ + _i++ \ +) + +#ifdef CONFIG_BES2600_BT +int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv); +void bes2600_btusb_uninit(struct usb_interface *interface); +#endif + +#endif /* BES2600_H */ diff --git a/drivers/staging/bes2600/bes2600_btusb.c b/drivers/staging/bes2600/bes2600_btusb.c new file mode 100644 index 000000000000..621065e2a18d --- /dev/null +++ b/drivers/staging/bes2600/bes2600_btusb.c @@ -0,0 +1,633 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include "bes2600.h" + +#define BES2600_BTUSB_MAX_BULK_TX 2 +#define BES2600_BTUSB_MAX_BULK_RX 2 + +enum { + CQ_OK = 0, + CQ_ERR, +}; + +typedef unsigned char CQItemType; + +typedef struct __CQueue +{ + int read; + int write; + int size; + int len; + CQItemType *base; +}CQueue; +struct bes2600_btusb_data { + struct usb_device *udev; + struct bes2600_usb_pipe* rx_pipes; + struct bes2600_usb_pipe* tx_pipes; + struct hci_dev *hdev; + + unsigned long state; + + rwlock_t lock; + + struct sk_buff_head transmit_q; + struct work_struct tx_work; + + atomic_t pending_tx; + struct sk_buff_head pending_q; + CQueue bt_rx_queue; + unsigned char * rx_buffer; +}; + +struct bes2600_btusb_data_scb { + struct urb *urb; +}; + +static int bes2600_btusb_rx_submit(struct bes2600_btusb_data *data, struct urb *urb); + +int InitCQueue(CQueue *Q, unsigned int size, CQItemType *buf) +{ + Q->size = size; + Q->base = buf; + Q->len = 0; + if (!Q->base) + return CQ_ERR; + + Q->read = Q->write = 0; + return CQ_OK; +} + +int IsEmptyCQueue(CQueue *Q) +{ + if (Q->len == 0) + return CQ_OK; + else + return CQ_ERR; +} + +int LengthOfCQueue(CQueue *Q) +{ + return Q->len; +} + +int AvailableOfCQueue(CQueue *Q) +{ + return (Q->size - Q->len); +} + +int EnCQueue(CQueue *Q, CQItemType *e, unsigned int len) +{ + if (AvailableOfCQueue(Q) < len) { + return CQ_ERR; + } + + Q->len += len; + + uint32_t bytesToTheEnd = Q->size - Q->write; + if (bytesToTheEnd > len) + { + memcpy((uint8_t *)&Q->base[Q->write], (uint8_t *)e, len); + Q->write += len; + } + else + { + memcpy((uint8_t *)&Q->base[Q->write], (uint8_t *)e, bytesToTheEnd); + memcpy((uint8_t *)&Q->base[0], (((uint8_t *)e)+bytesToTheEnd), len-bytesToTheEnd); + Q->write = len-bytesToTheEnd; + } + + return CQ_OK; +} + +int PeekCQueue(CQueue *Q, unsigned int len_want, CQItemType **e1, unsigned int *len1, CQItemType **e2, unsigned int *len2) +{ + if(LengthOfCQueue(Q) < len_want) { + return CQ_ERR; + } + + *e1 = &(Q->base[Q->read]); + if((Q->write > Q->read) || (Q->size - Q->read >= len_want)) { + *len1 = len_want; + *e2 = NULL; + *len2 = 0; + return CQ_OK; + } + else { + *len1 = Q->size - Q->read; + *e2 = &(Q->base[0]); + *len2 = len_want - *len1; + return CQ_OK; + } + + return CQ_ERR; +} + +int PeekCQueueToBuf(CQueue *Q, CQItemType *e, unsigned int len) +{ + int status = CQ_OK; + unsigned char *e1 = NULL, *e2 = NULL; + unsigned int len1 = 0, len2 = 0; + + status = PeekCQueue(Q, len, &e1, &len1, &e2, &len2); + + if(status == CQ_OK) { + if (len == (len1 + len2)) { + memcpy(e, e1, len1); + memcpy(e + len1, e2, len2); + } else { + status = CQ_ERR; + } + } + + return status; +} + +int DeCQueue(CQueue *Q, CQItemType *e, unsigned int len) +{ + if(LengthOfCQueue(Q) < len) + return CQ_ERR; + + Q->len -= len; + + if(e != NULL) + { + uint32_t bytesToTheEnd = Q->size - Q->read; + if (bytesToTheEnd > len) + { + memcpy((uint8_t *)e, (uint8_t *)&Q->base[Q->read], len); + Q->read += len; + } + else + { + memcpy((uint8_t *)e, (uint8_t *)&Q->base[Q->read], bytesToTheEnd); + memcpy((((uint8_t *)e)+bytesToTheEnd), (uint8_t *)&Q->base[0], len-bytesToTheEnd); + Q->read = len-bytesToTheEnd; + } + } + else + { + if (0 < Q->size) + { + Q->read = (Q->read+len)%Q->size; + } + else + { + Q->read = 0; + } + } + + return CQ_OK; +} + +static int bes2600_btusb_recv_bulk(struct bes2600_btusb_data *data, void *buffer, int count) +{ + struct sk_buff *skb = NULL; + int err = 0; + unsigned char bt_rx_hdr[5]; + unsigned short frame_len = 0; + unsigned char head_length = 0; + unsigned char * raw_data = NULL; + { + EnCQueue(&data->bt_rx_queue, (CQItemType*)buffer, count); + + while(1){ + frame_len = 0; + head_length = 0; + if(LengthOfCQueue(&data->bt_rx_queue) >= 4){ + PeekCQueueToBuf(&data->bt_rx_queue, (CQItemType*)bt_rx_hdr, 4); + if(bt_rx_hdr[0] == HCI_EVENT_PKT){ + head_length = 3; + frame_len = bt_rx_hdr[2] + head_length; + }else if(bt_rx_hdr[0] == HCI_ACLDATA_PKT){ + head_length = 5; + if(LengthOfCQueue(&data->bt_rx_queue) >= head_length){ + PeekCQueueToBuf(&data->bt_rx_queue, (CQItemType*)(bt_rx_hdr), head_length); + }else{ + break; + } + frame_len = (bt_rx_hdr[3] | (bt_rx_hdr[4] << 8)) + head_length; + }else if(bt_rx_hdr[0] == HCI_SCODATA_PKT){ + head_length = 4; + frame_len = bt_rx_hdr[3] + head_length; + }else{ + bes2600_err(BES2600_DBG_BT, "Invalid packet type:%02x", bt_rx_hdr[0]); + } + }else{ + break; + } + + bes2600_dbg(BES2600_DBG_BT, "btrx flen:%d", frame_len); + + if(LengthOfCQueue(&data->bt_rx_queue) >= frame_len){ + skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!skb) { + err = -ENOMEM; + break; + } + hci_skb_pkt_type(skb) = bt_rx_hdr[0]; + raw_data = skb_put(skb, frame_len); + PeekCQueueToBuf(&data->bt_rx_queue, (CQItemType*)raw_data, frame_len); + //jump packet type + skb_pull(skb, 1); + DeCQueue(&data->bt_rx_queue, NULL, frame_len); + bes2600_dbg_dump(BES2600_DBG_BT, "btrx flen:", raw_data, 8); + /* Complete frame */ + hci_recv_frame(data->hdev, skb); + }else{ + break; + } + } + } + + return err; +} + + +static void bes2600_btusb_rx_complete(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct bes2600_btusb_data *data = (struct bes2600_btusb_data *) skb->dev; + int count = urb->actual_length; + int err; + + bes2600_dbg(BES2600_DBG_BT, "btusb_rx_complete %p urb %p skb %p len %d %d", data, urb, skb, skb->len, urb->actual_length); + read_lock(&data->lock); + + if (!test_bit(HCI_RUNNING, &data->hdev->flags)){ + bes2600_dbg(BES2600_DBG_BT, "rx_complete data->hdev->flags=%x", data->hdev->flags); + goto unlock; + } + + if (urb->status || !count){ + bes2600_dbg(BES2600_DBG_BT, "rx_complete urb->status:%d count:%d"); + goto resubmit; + } + + data->hdev->stat.byte_rx += count; + + bes2600_btusb_recv_bulk(data, urb->transfer_buffer, urb->actual_length); + + skb_unlink(skb, &data->pending_q); + kfree_skb(skb); + + bes2600_btusb_rx_submit(data, urb); + + read_unlock(&data->lock); + + return; + +resubmit: + urb->dev = data->udev; + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + bes2600_err(BES2600_DBG_BT, "%s bulk resubmit failed urb %p err %d", + data->hdev->name, urb, err); + } + +unlock: + read_unlock(&data->lock); +} + + +static int bes2600_btusb_rx_submit(struct bes2600_btusb_data *data, struct urb *urb) +{ + struct bes2600_btusb_data_scb *scb; + struct sk_buff *skb; + struct bes2600_usb_pipe* recv_pipe = NULL; + int err, size = HCI_MAX_FRAME_SIZE + 32; + + bes2600_dbg(BES2600_DBG_BT, "btusb_rx_submit %p urb %p", data, urb); + + if (!urb) { + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + } + + skb = bt_skb_alloc(size, GFP_ATOMIC); + if (!skb) { + usb_free_urb(urb); + return -ENOMEM; + } + + skb->dev = (void *) data; + + scb = (struct bes2600_btusb_data_scb *) skb->cb; + scb->urb = urb; + + recv_pipe = data->rx_pipes; + usb_fill_bulk_urb(urb, data->udev, data->rx_pipes->usb_pipe_handle, skb->data, size, + bes2600_btusb_rx_complete, skb); + + bes2600_dbg(BES2600_DBG_BT, "btusb_rx: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n", + recv_pipe->logical_pipe_num, + recv_pipe->usb_pipe_handle, recv_pipe->ep_address, + size, skb); + skb_queue_tail(&data->pending_q, skb); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + bes2600_err(BES2600_DBG_BT, "%s bulk rx submit failed urb %p err %d", + data->hdev->name, urb, err); + skb_unlink(skb, &data->pending_q); + kfree_skb(skb); + usb_free_urb(urb); + } + + return err; +} + +static int bes2600_btusb_open(struct hci_dev *hdev) +{ + struct bes2600_btusb_data *data = hci_get_drvdata(hdev); + unsigned long flags; + int i, err; + + write_lock_irqsave(&data->lock, flags); + + err = bes2600_btusb_rx_submit(data, NULL); + if (!err) { + for (i = 1; i < BES2600_BTUSB_MAX_BULK_RX; i++) + bes2600_btusb_rx_submit(data, NULL); + } + + write_unlock_irqrestore(&data->lock, flags); + return err; +} + +static void bes2600_btusb_unlink_urbs(struct bes2600_btusb_data *data) +{ + struct sk_buff *skb; + struct urb *urb; + + while ((skb = skb_dequeue(&data->pending_q))) { + urb = ((struct bes2600_btusb_data_scb *) skb->cb)->urb; + usb_kill_urb(urb); + kfree_skb(skb); + } +} + +static int bes2600_btusb_flush(struct hci_dev *hdev) +{ + struct bes2600_btusb_data *data = hci_get_drvdata(hdev); + + bes2600_dbg(BES2600_DBG_BT, "btusb_flush hdev %p btusb %p", hdev, data); + read_lock(&data->lock); + skb_queue_purge(&data->transmit_q); + read_unlock(&data->lock); + + return 0; +} + +static int bes2600_btusb_close(struct hci_dev *hdev) +{ + struct bes2600_btusb_data *data = hci_get_drvdata(hdev); + unsigned long flags; + + bes2600_dbg(BES2600_DBG_BT, "btusb_close hdev %p btusb %p", hdev, data); + write_lock_irqsave(&data->lock, flags); + write_unlock_irqrestore(&data->lock, flags); + + bes2600_btusb_unlink_urbs(data); + bes2600_btusb_flush(hdev); + + return 0; +} + +static int bes2600_btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + char *pkt_type = NULL; + struct bes2600_btusb_data *data = hci_get_drvdata(hdev); + + bes2600_dbg(BES2600_DBG_BT, "btusb_send_frame hdev %p skb %p type %d len %d\n", hdev, skb, + hci_skb_pkt_type(skb), skb->len); + bes2600_dbg_dump(BES2600_DBG_BT, "Frame Content:", skb->data, 9) + + switch (hci_skb_pkt_type(skb)) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + } + pkt_type = skb_push(skb, 1); + pkt_type[0] = hci_skb_pkt_type(skb); + read_lock(&data->lock); + skb_queue_tail(&data->transmit_q, skb); + read_unlock(&data->lock); + + schedule_work(&data->tx_work); + return 0; +} + +static void bes2600_btusb_notify(struct hci_dev *hdev, unsigned int evt) +{ + struct bes2600_btusb_data *data = hci_get_drvdata(hdev); + + bes2600_dbg(BES2600_DBG_BT, "btusb_notify %s evt %d", hdev->name, evt); + if(evt == HCI_NOTIFY_CONN_ADD){ + + }else if(evt == HCI_NOTIFY_CONN_DEL){ + + }else if(evt == HCI_NOTIFY_VOICE_SETTING){ + + } +} + +static inline int bes2600_btusb_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + bes2600_dbg(BES2600_DBG_BT, "bes2600_btusb_set_bdaddr"); + return -EOPNOTSUPP; +} + +static void bes2600_btusb_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct bes2600_btusb_data *data = (struct bes2600_btusb_data *) skb->dev; + + bes2600_dbg(BES2600_DBG_BT, "btusb_tx_complete %p urb %p skb %p len %d", data, urb, skb, skb->len); + atomic_dec(&data->pending_tx); + + if (!test_bit(HCI_RUNNING, &data->hdev->flags)) + return; + + if (!urb->status) + data->hdev->stat.byte_tx += skb->len; + else + data->hdev->stat.err_tx++; + + read_lock(&data->lock); + skb_unlink(skb, &data->pending_q); + read_unlock(&data->lock); + kfree_skb(skb); + + if(skb_queue_len(&data->transmit_q)) + schedule_work(&data->tx_work); + +} + +static int bes2600_btusb_send_bulk(struct bes2600_btusb_data *data, struct sk_buff *skb) +{ + struct bes2600_btusb_data_scb *scb = (void *) skb->cb; + struct urb *urb = NULL; + int err; + struct bes2600_usb_pipe* tx_pipe = data->tx_pipes; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + skb->dev = (void*)data; + usb_fill_bulk_urb(urb, data->udev, tx_pipe->usb_pipe_handle, skb->data, skb->len, + bes2600_btusb_tx_complete, skb); + + bes2600_dbg(BES2600_DBG_BT, "btusb_tx: bulk tx submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n", + tx_pipe->logical_pipe_num, + tx_pipe->usb_pipe_handle, tx_pipe->ep_address, + skb->len, skb); + + scb->urb = urb; + + skb_queue_tail(&data->pending_q, skb); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + bes2600_err(BES2600_DBG_BT, "%s bulk tx submit failed urb %p err %d", + data->hdev->name, urb, err); + skb_unlink(skb, &data->pending_q); + } else + atomic_inc(&data->pending_tx); + + usb_free_urb(urb); + return err; +} + +static void bes2600_btusb_work(struct work_struct *work) +{ + struct bes2600_btusb_data *data = container_of(work, struct bes2600_btusb_data, tx_work); + struct sk_buff *skb; + read_lock(&data->lock); + while ((atomic_read(&data->pending_tx) < BES2600_BTUSB_MAX_BULK_TX) && + (skb = skb_dequeue(&data->transmit_q))) { + if (bes2600_btusb_send_bulk(data, skb) < 0) { + skb_queue_head(&data->transmit_q, skb); + break; + } + } + read_unlock(&data->lock); +} + +int bes2600_btusb_setup_pipes(struct sbus_priv *ar_usb) +{ + struct hci_dev *hdev; + struct bes2600_btusb_data *data; + + /* Initialize control structure */ + data = kzalloc(sizeof(struct bes2600_btusb_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rx_buffer = kzalloc(8*1024, GFP_KERNEL); + InitCQueue(&data->bt_rx_queue, 8*1024, data->rx_buffer); + + rwlock_init(&data->lock); + + skb_queue_head_init(&data->transmit_q); + skb_queue_head_init(&data->pending_q); + + /* Initialize and register HCI device */ + hdev = hci_alloc_dev(); + if (!hdev) { + bes2600_err(BES2600_DBG_BT, "bes2600 Can't allocate HCI device"); + goto done; + } + + data->udev = ar_usb->udev; + data->rx_pipes = &ar_usb->pipes[BES2600_USB_PIPE_BT_RX_DATA]; + data->tx_pipes = &ar_usb->pipes[BES2600_USB_PIPE_BT_TX_DATA]; + data->hdev = hdev; + INIT_WORK(&data->tx_work, bes2600_btusb_work); + + hdev->bus = HCI_USB; + hdev->dev_type = HCI_PRIMARY; + hci_set_drvdata(hdev, data); + SET_HCIDEV_DEV(hdev, &ar_usb->interface->dev); + hdev->open = bes2600_btusb_open; + hdev->close = bes2600_btusb_close; + hdev->flush = bes2600_btusb_flush; + hdev->send = bes2600_btusb_send_frame; + hdev->notify = bes2600_btusb_notify; + hdev->set_bdaddr = bes2600_btusb_set_bdaddr; + + //set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); + + if (hci_register_dev(hdev) < 0) { + bes2600_err(BES2600_DBG_BT, "Can't register HCI device"); + hci_free_dev(hdev); + goto done; + } + bes2600_info(BES2600_DBG_BT, "register HCI device ok:%p %p",hdev, data); + ar_usb->btdev = data; + return 0; + +done: + return -EIO; +} + +void bes2600_btusb_uninit(struct usb_interface *interface) +{ + struct bes2600_btusb_data *data = NULL; + struct sbus_priv *ar_usb = NULL; + struct hci_dev *hdev = NULL; + + ar_usb = usb_get_intfdata(interface); + if (ar_usb == NULL) + return; + + data = (struct bes2600_btusb_data*)ar_usb->btdev; + if(!data) + return; + + hdev = data->hdev; + cancel_work_sync(&data->tx_work); + if (!hdev) + return; + + bes2600_btusb_close(hdev); + kfree(data->rx_buffer); + kfree(data); + hci_unregister_dev(hdev); + hci_free_dev(hdev); +} + + diff --git a/drivers/staging/bes2600/bes2600_cfgvendor.c b/drivers/staging/bes2600/bes2600_cfgvendor.c new file mode 100644 index 000000000000..220f86018a46 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_cfgvendor.c @@ -0,0 +1,1063 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bes2600_cfgvendor.h" + +void bes2600_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + const struct ieee80211_regdomain *tmp = NULL; + + /* If wiphy->regd is not cleared, reopening the SoftAP after the sta disconnects from an AP will fail */ + if(request->initiator == NL80211_REGDOM_SET_BY_CORE && + !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) { + tmp = rtnl_dereference(wiphy->regd); + if(tmp) { + rcu_assign_pointer(wiphy->regd, NULL); + kfree_rcu((struct ieee80211_regdomain *)tmp, rcu_head); + bes2600_info(BES2600_DBG_ANDROID, "clear regdom when sta disconnects from an ap.\n"); + } + } +} + +static int bes2600_set_country_code(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ +#define CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */ + int rem, type; + char country_code[CNTRY_BUF_SZ] = {0}; + const struct nlattr *iter; + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case ANDR_WIFI_ATTRIBUTE_COUNTRY: + memcpy(country_code, nla_data(iter), + MIN(nla_len(iter), CNTRY_BUF_SZ)); + break; + default: + return -EINVAL; + } + } + + /* check whether the country is valid or not */ + if(!isalpha(country_code[0]) || + !isalpha(country_code[1])) { + return -EINVAL; + } + + /* notify cfg80211 to update database */ + return regulatory_hint(wiphy, country_code); +} + +#if 0 +static int bes2600_cfgvendor_gscan_get_capabilities(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + dhd_pno_gscan_capabilities_t *reply = NULL; + unsigned int reply_len = 0; + + // TODO:do something + + if (unlikely(err)) + // TODO:do something + + kfree(reply); + return err; +} + + +static int bes2600_cfgvendor_set_scan_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + // TODO:do something + +} + + +static int bes2600_cfgvendor_set_batch_scan_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0, tmp, type; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + gscan_batch_params_t batch_param; + const struct nlattr *iter; + + batch_param.mscan = batch_param.bestn = 0; + batch_param.buffer_threshold = GSCAN_BATCH_NO_THR_SET; + + nla_for_each_attr(iter, data, len, tmp) { + type = nla_type(iter); + + + } + // TODO:do something + return err; +} + + +static int bes2600_cfgvendor_initiate_gscan(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + int type, tmp = len; + int run = 0xFF; + int flush = 0; + const struct nlattr *iter; + + nla_for_each_attr(iter, data, len, tmp) { + type = nla_type(iter); + } + // TODO:do something + return err; + +} + + +static int bes2600_cfgvendor_enable_full_scan_result(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + int type; + bool real_time = FALSE; + + type = nla_type(data); + + // TODO:do something + + return err; +} + + +static int bes2600_cfgvendor_hotlist_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + // gscan_hotlist_scan_params_t *hotlist_params; + int tmp, tmp1, tmp2, type, j = 0, dummy; + const struct nlattr *outer, *inner, *iter; + unsigned char flush = 0; + struct bssid_t *pbssid; + + // TODO:do something + + nla_for_each_attr(iter, data, len, tmp2) { + type = nla_type(iter); + switch (type) { + + } + // TODO:do something + goto exit; + } +exit: + + return err; +} + + +static int bes2600_cfgvendor_significant_change_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + // gscan_swc_params_t *significant_params; + int tmp, tmp1, tmp2, type, j = 0; + const struct nlattr *outer, *inner, *iter; + unsigned char flush = 0; + // wl_pfn_significant_bssid_t *pbssid; + + // TODO:do something + + nla_for_each_attr(iter, data, len, tmp2) { + type = nla_type(iter); + + switch (type) { + + } +} + + // TODO:do something +exit: + + return err; +} + + +static int bes2600_cfgvendor_gscan_get_batch_results(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + // gscan_results_cache_t *results, *iter; + unsigned int reply_len, complete = 0, num_results_iter; + int mem_needed; + // wifi_gscan_result_t *ptr; + unsigned short num_scan_ids, num_results; + struct sk_buff *skb; + struct nlattr *scan_hdr; + + // TODO:do something + num_scan_ids = reply_len & 0xFFFF; + num_results = (reply_len & 0xFFFF0000) >> 16; + + // TODO:do something + return err; +} + + +static int bes2600_cfgvendor_gscan_get_channel_list(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0, type, band; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + unsigned short *reply = NULL; + unsigned int reply_len = 0, num_channels, mem_needed; + struct sk_buff *skb; + + type = nla_type(data); + + // TODO:do something + + if (!reply) { + // TODO:do something + err = -EINVAL; + return err; + } + + // TODO:do something + if (unlikely(!skb)) { + // TODO:do something + err = -ENOMEM; + goto exit; + } + + // TODO:do something + + if (unlikely(err)) + // TODO:do something +exit: + kfree(reply); + return err; +} + + +static int bes2600_cfgvendor_rtt_set_config(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int len) +{ + int err = 0, rem, rem1, rem2, type; + + const struct nlattr *iter, *iter1, *iter2; + + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + + if (err < 0) { + // TODO:do something + goto exit; + } + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + // TODO:do something + } + } + +exit: + return err; +} + + +static int bes2600_cfgvendor_rtt_cancel_config(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int len) +{ + int err = 0, rem, type, target_cnt = 0; + const struct nlattr *iter; + struct ether_addr *mac_list = NULL, *mac_addr = NULL; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + + } + } +exit: + if (mac_list) + kfree(mac_list); + return err; +} + + +static int bes2600_cfgvendor_rtt_get_capability(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int len) +{ + int err = 0; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + + if (unlikely(err)) { + // TODO:do something + goto exit; + } + // TODO:do something + + if (unlikely(err)) + // TODO:do something +exit: + return err; +} + + +static int bes2600_cfgvendor_lstats_get_info(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + + char *output; + + // TODO:do something + if (output == NULL) { + // TODO:do something + } + + return err; +} + + +static int bes2600_cfgvendor_lstats_set_info(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + // TODO:do something + return err; +} + + +static int bes2600_cfgvendor_lstats_clear_info(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + // TODO:do something + return err; +} + + +static int bes2600_cfgvendor_set_rssi_monitor(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + // TODO:do something + + int err = 0, rem, type; + const struct nlattr *iter; + + // TODO:do something + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + + switch (type) { + // TODO:do something + } + } + + return err; +} + + +static int bes2600_cfgvendor_logger_start_logging(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = 0, rem, type; + char ring_name[32] = {0}; + int log_level = 0, flags = 0, time_intval = 0, threshold = 0; + const struct nlattr *iter; + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + // TODO:do something + } + } + +exit: + return ret; +} + + +static int bes2600_cfgvendor_logger_get_feature(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + u32 supported_features = 0; + + if (unlikely(err)) + // TODO:do something + + return err; +} + + +static int bes2600_cfgvendor_logger_get_version(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + + int ret = 0, rem, type; + int buf_len = 1024; + char *buf_ptr; + const struct nlattr *iter; + gfp_t kflags; + + kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL; + buf_ptr = kzalloc(buf_len, kflags); + if (!buf_ptr) { + // TODO:do something + ret = -ENOMEM; + goto exit; + } + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + // TODO:do something + } + } + if (ret < 0) { + // TODO:do something + goto exit; + } + + + // TODO:do something +exit: + kfree(buf_ptr); + return ret; +} + + +static int bes2600_cfgvendor_logger_get_ring_status(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = 0; + int ring_id; + char ring_buf_name[] = "bes2600_RING_BUFFER"; + + struct sk_buff *skb; + + // TODO:do something + /* Alloc the SKB for vendor_event */ + + if (!skb) { + // TODO:do something + ret = FAIL; + goto exit; + } + + ret = cfg80211_vendor_cmd_reply(skb); + + if (ret) { + // TODO:do something + } +exit: + return ret; +} + + +static int bes2600_cfgvendor_logger_get_ring_data(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = 0, rem, type; + char ring_name[32] = {0}; + const struct nlattr *iter; + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + + // TODO:do something + return ret; + } + } + + return ret; +} + + +static int bes2600_cfgvendor_logger_get_firmware_memory_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = WIFI_ERROR_NOT_SUPPORTED; + + return ret; +} + + +static int bes2600_cfgvendor_logger_start_pkt_fate_monitoring(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = WIFI_SUCCESS; + + return ret; +} + + +static int bes2600_cfgvendor_logger_get_tx_pkt_fates(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = WIFI_SUCCESS; + + return ret; +} + + +static int bes2600_cfgvendor_logger_get_rx_pkt_fates(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int ret = WIFI_SUCCESS; + + return ret; +} + + +static int bes2600_cfgvendor_set_rand_mac_oui(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + + void *devaddr; + struct net_device *netdev; + int type, mac_len; + u8 pno_random_mac_oui[3]; + u8 mac_addr[ETH_ALEN] = {0}; + struct pwrctrl_priv *pwrctl; + // TODO:do something + + type = nla_type(data); + mac_len = nla_len(data); + if (mac_len != 3) { + // TODO:do something + return -1; + } + + if (type == ANDR_WIFI_ATTRIBUTE_RANDOM_MAC_OUI) { + memcpy(pno_random_mac_oui, nla_data(data), 3); + print_hex_dump(KERN_DEBUG, "pno_random_mac_oui: ", + DUMP_PREFIX_OFFSET, 16, 1, pno_random_mac_oui, + 3, 1); + + // TODO:do something + } else { + // TODO:do something + err = -1; + } + + + return err; +} + + +static int bes2600_cfgvendor_set_nodfs_flag(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + int type; + u32 nodfs = 0; + + // TODO:do something + + type = nla_type(data); + if (type == ANDR_WIFI_ATTRIBUTE_NODFS_SET) { + nodfs = nla_get_u32(data); + + } else { + err = -EINVAL; + } + + // TODO:do something + + return err; +} + + +static int bes2600_cfgvendor_set_nd_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + int type; + u8 nd_en = 0; + + // TODO:do something + + type = nla_type(data); + if (type == ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE) { + nd_en = nla_get_u8(data); + /* ND has been enabled when wow is enabled */ + } else { + err = -EINVAL; + } + + return err; +} + + +static int bes2600_cfgvendor_get_feature_set(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + int reply; + + // TODO:do something + if (unlikely(err)) + // TODO:do something + + return err; +} + + +static int bes2600_cfgvendor_get_feature_set_matrix(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, int len) +{ + int err = 0; + struct sk_buff *skb; + int *reply; + int num, mem_needed, i; + + // TODO:do something + + if (!reply) { + // TODO:do something + err = -EINVAL; + return err; + } + + /* Alloc the SKB for vendor_event */ + // TODO:do something + if (unlikely(!skb)) { + // TODO:do something + err = -ENOMEM; + goto exit; + } + + nla_put_u32(skb, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, num); + for (i = 0; i < num; i++) + nla_put_u32(skb, ANDR_WIFI_ATTRIBUTE_FEATURE_SET, reply[i]); + + // TODO:do something + + if (unlikely(err)) + // TODO:do something +exit: + // TODO:do something + return err; +} +#endif + +static const struct wiphy_vendor_command bes2600_own_commands[] = { + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_SET_COUNTRY_CODE + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_set_country_code, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + + }, + #if 0 + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_GET_CAPABILITIES + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_gscan_get_capabilities +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_SET_CONFIG + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_set_scan_cfg +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_SET_SCAN_CONFIG + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_set_batch_scan_cfg +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_ENABLE_GSCAN + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_initiate_gscan +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_enable_full_scan_result +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_SET_HOTLIST + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_hotlist_cfg +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_significant_change_cfg +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_GET_SCAN_RESULTS + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_gscan_get_batch_results +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = GSCAN_SUBCMD_GET_CHANNEL_LIST + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_gscan_get_channel_list +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = RTT_SUBCMD_SET_CONFIG + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_rtt_set_config +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = RTT_SUBCMD_CANCEL_CONFIG + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_rtt_cancel_config +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = RTT_SUBCMD_GETCAPABILITY + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_rtt_get_capability +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LSTATS_SUBCMD_GET_INFO + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_lstats_get_info +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LSTATS_SUBCMD_SET_INFO + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_lstats_set_info +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LSTATS_SUBCMD_CLEAR_INFO + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_lstats_clear_info +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_SET_RSSI_MONITOR + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_set_rssi_monitor +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_START_LOGGING + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_start_logging +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_GET_FEATURE + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_feature +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_GET_VER + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_version +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_GET_RING_STATUS + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_ring_status +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_GET_RING_DATA + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_ring_data +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_TRIGGER_MEM_DUMP + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_firmware_memory_dump +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_START_PKT_FATE_MONITORING + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_start_pkt_fate_monitoring +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_GET_TX_PKT_FATES + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_tx_pkt_fates +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = LOGGER_GET_RX_PKT_FATES + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_logger_get_rx_pkt_fates +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_set_rand_mac_oui +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_NODFS_SET + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_set_nodfs_flag +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_CONFIG_ND_OFFLOAD + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_set_nd_offload +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_GET_FEATURE_SET + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_get_feature_set +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + { + .vendor_id = OUI_GOOGLE, + .subcmd = WIFI_SUBCMD_GET_FEATURE_SET_MATRIX + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = bes2600_cfgvendor_get_feature_set_matrix +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif + } + #endif +}; + +static const struct nl80211_vendor_cmd_info bes2600_own_events[] = { + { OUI_GOOGLE, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS }, + { OUI_GOOGLE, GSCAN_EVENT_HOTLIST_RESULTS_FOUND }, + { OUI_GOOGLE, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE }, + { OUI_GOOGLE, GSCAN_EVENT_FULL_SCAN_RESULTS }, + { OUI_GOOGLE, RTT_EVENT_COMPLETE }, + { OUI_GOOGLE, GOOGLE_RSSI_MONITOR_EVENT }, + { OUI_GOOGLE, GSCAN_EVENT_COMPLETE_SCAN }, + { OUI_GOOGLE, GSCAN_EVENT_HOTLIST_RESULTS_LOST } + +}; + + +int bes2600_set_vendor_command(struct wiphy *wiphy) +{ + + wiphy->vendor_commands = bes2600_own_commands; + wiphy->n_vendor_commands = ARRAY_SIZE(bes2600_own_commands); + wiphy->vendor_events = bes2600_own_events; + wiphy->n_vendor_events = ARRAY_SIZE(bes2600_own_events); + + return 0; +} + +int bes2600_vendor_command_detach(struct wiphy *wiphy) +{ + + wiphy->vendor_commands = NULL; + wiphy->vendor_events = NULL; + wiphy->n_vendor_commands = 0; + wiphy->n_vendor_events = 0; + + return 0; +} + + + + diff --git a/drivers/staging/bes2600/bes2600_cfgvendor.h b/drivers/staging/bes2600/bes2600_cfgvendor.h new file mode 100644 index 000000000000..66d400d12316 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_cfgvendor.h @@ -0,0 +1,218 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef BES2600_CFGVENDOR_H +#define BES2600_CFGVENDOR_H + +#include "bes2600.h" +#include "txrx.h" +#include "sbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "ap.h" +#include "scan.h" +#include "debug.h" +#include "pm.h" + +#define OUI_GOOGLE 0x001A11 +#define GSCAN_BATCH_NO_THR_SET 101 +#define FALSE 0 +#define FAIL (-1) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +typedef enum bes2600_own_event { + BES2600_RESERVED1, + BES2600_RESERVED2, + GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS , + GSCAN_EVENT_HOTLIST_RESULTS_FOUND, + GSCAN_EVENT_SCAN_RESULTS_AVAILABLE, + GSCAN_EVENT_FULL_SCAN_RESULTS, + RTT_EVENT_COMPLETE, + GSCAN_EVENT_COMPLETE_SCAN, + GSCAN_EVENT_HOTLIST_RESULTS_LOST, + GSCAN_EVENT_EPNO_EVENT, + GOOGLE_DEBUG_RING_EVENT, + GOOGLE_DEBUG_MEM_DUMP_EVENT, + GSCAN_EVENT_ANQPO_HOTSPOT_MATCH, + GOOGLE_RSSI_MONITOR_EVENT +} bes2600_own_event_t; + +enum andr_wifi_feature_set_attr { + ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, + ANDR_WIFI_ATTRIBUTE_FEATURE_SET, + ANDR_WIFI_ATTRIBUTE_RANDOM_MAC_OUI, + ANDR_WIFI_ATTRIBUTE_NODFS_SET, + ANDR_WIFI_ATTRIBUTE_COUNTRY, + ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE + // Add more attribute here +}; + +typedef enum { + /* don't use 0 as a valid subcommand */ + VENDOR_NL80211_SUBCMD_UNSPECIFIED, + + /* define all vendor startup commands between 0x0 and 0x0FFF */ + VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001, + VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF, + + /* define all GScan related commands between 0x1000 and 0x10FF */ + ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000, + ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF, + + /* define all NearbyDiscovery related commands between 0x1100 and 0x11FF */ + ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1100, + ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x11FF, + + /* define all RTT related commands between 0x1100 and 0x11FF */ + ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100, + ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF, + + ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200, + ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF, + + /* define all Logger related commands between 0x1400 and 0x14FF */ + ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400, + ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF, + + /* define all wifi offload related commands between 0x1600 and 0x16FF */ + ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600, + ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF, + + /* define all NAN related commands between 0x1700 and 0x17FF */ + ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700, + ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF, + + /* define all Android Packet Filter related commands between 0x1800 and 0x18FF */ + ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800, + ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF, + + /* This is reserved for future usage */ + +} ANDROID_VENDOR_SUB_COMMAND; + +enum { + LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START, + LSTATS_SUBCMD_SET_INFO, + LSTATS_SUBCMD_CLEAR_INFO, +}; + +enum bes2600_vendor_subcmd { + GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START, + + GSCAN_SUBCMD_SET_CONFIG, /* 0x1001 */ + + GSCAN_SUBCMD_SET_SCAN_CONFIG, /* 0x1002 */ + GSCAN_SUBCMD_ENABLE_GSCAN, /* 0x1003 */ + GSCAN_SUBCMD_GET_SCAN_RESULTS, /* 0x1004 */ + GSCAN_SUBCMD_SCAN_RESULTS, /* 0x1005 */ + + GSCAN_SUBCMD_SET_HOTLIST, /* 0x1006 */ + + GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG, /* 0x1007 */ + GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, /* 0x1008 */ + GSCAN_SUBCMD_GET_CHANNEL_LIST, /* 0x1009 */ + + WIFI_SUBCMD_GET_FEATURE_SET, /* 0x100A */ + WIFI_SUBCMD_GET_FEATURE_SET_MATRIX, /* 0x100B */ + WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, /* 0x100C */ + WIFI_SUBCMD_NODFS_SET, /* 0x100D */ + WIFI_SUBCMD_SET_COUNTRY_CODE, /* 0x100E */ + /* Add more sub commands here */ + GSCAN_SUBCMD_SET_EPNO_SSID, /* 0x100F */ + + WIFI_SUBCMD_SET_SSID_WHITE_LIST, /* 0x1010 */ + WIFI_SUBCMD_SET_ROAM_PARAMS, /* 0x1011 */ + WIFI_SUBCMD_ENABLE_LAZY_ROAM, /* 0x1012 */ + WIFI_SUBCMD_SET_BSSID_PREF, /* 0x1013 */ + WIFI_SUBCMD_SET_BSSID_BLACKLIST, /* 0x1014 */ + + GSCAN_SUBCMD_ANQPO_CONFIG, /* 0x1015 */ + WIFI_SUBCMD_SET_RSSI_MONITOR, /* 0x1016 */ + WIFI_SUBCMD_CONFIG_ND_OFFLOAD, /* 0x1017 */ + /* Add more sub commands here */ + + GSCAN_SUBCMD_MAX, + + RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START, + RTT_SUBCMD_CANCEL_CONFIG, + RTT_SUBCMD_GETCAPABILITY, + + APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START, + APF_SUBCMD_SET_FILTER, + + LOGGER_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START, + LOGGER_TRIGGER_MEM_DUMP, + LOGGER_GET_MEM_DUMP, + LOGGER_GET_VER, + LOGGER_GET_RING_STATUS, + LOGGER_GET_RING_DATA, + LOGGER_GET_FEATURE, + LOGGER_RESET_LOGGING, + LOGGER_TRIGGER_DRIVER_MEM_DUMP, + LOGGER_GET_DRIVER_MEM_DUMP, + LOGGER_START_PKT_FATE_MONITORING, + LOGGER_GET_TX_PKT_FATES, + LOGGER_GET_RX_PKT_FATES, + + VENDOR_SUBCMD_MAX +}; + +typedef enum { + WIFI_SUCCESS = 0, + WIFI_ERROR_NONE = 0, + WIFI_ERROR_UNKNOWN = -1, + WIFI_ERROR_UNINITIALIZED = -2, + WIFI_ERROR_NOT_SUPPORTED = -3, + WIFI_ERROR_NOT_AVAILABLE = -4, // Not available right now, but try later + WIFI_ERROR_INVALID_ARGS = -5, + WIFI_ERROR_INVALID_REQUEST_ID = -6, + WIFI_ERROR_TIMED_OUT = -7, + WIFI_ERROR_TOO_MANY_REQUESTS = -8, // Too many instances of this request + WIFI_ERROR_OUT_OF_MEMORY = -9, + WIFI_ERROR_BUSY = -10, +} wifi_error; + +enum _IFACE_ID { + IFACE_ID0, /*PRIMARY_ADAPTER*/ + IFACE_ID1, + IFACE_ID2, + IFACE_ID3, + IFACE_ID4, + IFACE_ID5, + IFACE_ID6, + IFACE_ID7, + IFACE_ID_MAX, +}; + +typedef struct dhd_pno_gscan_capabilities { + int max_scan_cache_size; + int max_scan_buckets; + int max_ap_cache_per_scan; + int max_rssi_sample_size; + int max_scan_reporting_threshold; + int max_hotlist_aps; + int max_significant_wifi_change_aps; + int max_epno_ssid_crc32; + int max_epno_hidden_ssid; + int max_white_list_ssid; +} dhd_pno_gscan_capabilities_t; + +typedef struct gscan_batch_params { + unsigned char bestn; + unsigned char mscan; + unsigned char buffer_threshold; +} gscan_batch_params_t; + +void bes2600_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); +int bes2600_set_vendor_command(struct wiphy *wiphy); +int bes2600_vendor_command_detach(struct wiphy *wiphy); +#endif \ No newline at end of file diff --git a/drivers/staging/bes2600/bes2600_driver_mode.h b/drivers/staging/bes2600/bes2600_driver_mode.h new file mode 100644 index 000000000000..a65aa1a8e78a --- /dev/null +++ b/drivers/staging/bes2600/bes2600_driver_mode.h @@ -0,0 +1,65 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _BES2600_DRIVER_MODE_CMD_ +#define _BES2600_DRIVER_MODE_CMD_ + +#ifdef FW_DOWNLOAD_BY_UART +#if defined(PLAT_ALLWINNER_T507) +#if defined(CONFIG_BES2600_WLAN_SDIO) +#define BES2600_LOAD_BOOT_NAME "/vendor/firmware/best2002_fw_boot_sdio.bin" +#define BES2600_LOAD_FW_NAME "/vendor/firmware/best2002_fw_sdio.bin" +#define BES2600_LOAD_NOSIGNAL_FW_NAME "/vendor/firmware/best2002_fw_sdio_nosignal_uart.bin" +#define BES2600_LOAD_BTRF_FW_NAME "/vendor/firmware/best2002_fw_sdio_btrf.bin" + +#elif defined(CONFIG_BES2600_WLAN_SPI) +#define BES2600_LOAD_BOOT_NAME "/vendor/firmware/best2002_fw_boot_spi.bin" +#define BES2600_LOAD_FW_NAME "/vendor/firmware/best2002_fw_spi.bin" +#define BES2600_LOAD_NOSIGNAL_FW_NAME "/vendor/firmware/best2002_fw_spi_nosignal.bin" +#define BES2600_LOAD_BTRF_FW_NAME "/vendor/firmware/best2002_fw_spi_btrf.bin" +#endif +#endif + +#if defined(PLAT_QCOM_QM215) +#define BES2600_LOAD_BOOT_NAME "/data/wifi/best2002_fw_boot_spi.bin" +#define BES2600_LOAD_FW_NAME "/data/wifi/best2002_fw_spi.bin" +#define BES2600_LOAD_NOSIGNAL_FW_NAME "/data/wifi/best2002_fw_spi_nosignal.bin" +#define BES2600_LOAD_BTRF_FW_NAME "/data/wifi/best2002_fw_spi_btrf.bin" +#endif +#endif + +#ifdef FW_DOWNLOAD_BY_SDIO +#ifdef CONFIG_FW_LOADER +#define BES2600_LOAD_BOOT_NAME "best2002_fw_boot_sdio.bin" +#define BES2600_LOAD_FW_NAME "best2002_fw_sdio.bin" +#define BES2600_LOAD_NOSIGNAL_FW_NAME "best2002_fw_sdio_nosignal.bin" +#define BES2600_LOAD_BTRF_FW_NAME "best2002_fw_sdio_btrf.bin" +#else +#define BES2600_LOAD_BOOT_NAME "/lib/firmware/best2002_fw_boot_sdio.bin" +#define BES2600_LOAD_FW_NAME "/lib/firmware/best2002_fw_sdio.bin" +#define BES2600_LOAD_NOSIGNAL_FW_NAME "/lib/firmware/best2002_fw_sdio_nosignal.bin" +#define BES2600_LOAD_BTRF_FW_NAME "/lib/firmware/best2002_fw_sdio_btrf.bin" +#endif +#ifdef BES2600_BOOT_UART_TO_SDIO +#define BES2600_LOAD_FW_TOOL_PATH "/usr/bin/bes_fw_download" +#define BES2600_LOAD_FW_TOOL_DEVICE "/dev/ttyS3" +#define BES2600_LOAD_BOOT_PATCH_NAME "/lib/firmware/bes2600_boot_patch.bin" +#endif +#endif + +#ifdef FW_DOWNLOAD_BY_USB +#define BES2600_LOAD_BOOT_NAME "best2002_fw_boot_usb.bin" +#define BES2600_LOAD_FW_NAME "best2002_fw_usb.bin" +#define BES2600_LOAD_NOSIGNAL_FW_NAME "best2002_fw_usb_nosignal.bin" +#define BES2600_LOAD_BTRF_FW_NAME "best2002_fw_usb_btrf.bin" +#endif + + +#endif /* _BES2600_DRIVER_MODE_CMD_ */ diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c new file mode 100644 index 000000000000..c525311c2a30 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_factory.c @@ -0,0 +1,930 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bes2600_factory.h" +#include "bes2600_log.h" +#include "bes_chardev.h" + +#define LE_CPU_TRANS(val, cvt) (val = cvt(val)) + +#define TRANS_LE16(val) LE_CPU_TRANS(val, __cpu_to_le16) +#define TRANS_LE32(val) LE_CPU_TRANS(val, __cpu_to_le32) + +#define TRANS_CPU16(val) LE_CPU_TRANS(val, __le16_to_cpu) +#define TRANS_CPU32(val) LE_CPU_TRANS(val, __le32_to_cpu) + +static DEFINE_MUTEX(factory_lock); + +/* + * It is only used for temporary storage. + * Every time get the factory, it will read from the + * file and overwrite the original value. + */ +static struct factory_t factory_cali_data; +static struct factory_t *factory_p = NULL; + +void bes2600_factory_lock(void) +{ + mutex_lock(&factory_lock); +} + +void bes2600_factory_unlock(void) +{ + mutex_unlock(&factory_lock); +} + +u8* bes2600_factory_get_file_buffer(void) +{ + u8 *file_buffer = NULL; + + file_buffer = kmalloc(FACTORY_MAX_SIZE, GFP_KERNEL); + if (!file_buffer) { + return NULL; + } + + return file_buffer; +} + +void bes2600_factory_free_file_buffer(u8 *file_buffer) +{ + if (file_buffer) + kfree(file_buffer); +} + +static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p); + +static inline uint32_t factory_crc32(const uint8_t *data, uint32_t len) +{ + u32 crc_le = 0; + crc_le = crc32_le(0xffffffffL, (uint8_t *)data, len); + crc_le ^= 0xffffffffL; + return crc_le; +} + +static int bes2600_factory_head_info_check(struct factory_t *factory_data) +{ + if (!factory_data) { + bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__); + return -1; + } + + if (factory_data->head.magic != NVREC_DEV_MAGIC) { + return -EBADMSG; + } + + if ((factory_data->head.version < NVREC_MINI_VERSION) || + (factory_data->head.version > NVREC_CURRENT_VERSION)) { + bes2600_err(BES2600_DBG_FACTORY, "factory version error:%d", factory_data->head.version); + return -EBADMSG; + } + + return 0; +} + +static int bes2600_factory_crc_check(struct factory_t *factory_data) +{ +#ifdef FACTORY_CRC_CHECK + u32 cal_crc = 0; + + if (!factory_data) { + bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err \n", __func__); + return -1; + } + + cal_crc = factory_crc32((uint8_t *)(&(factory_data->data)), sizeof(factory_data_t)); + if (factory_data->head.crc != cal_crc) { + bes2600_err(BES2600_DBG_CHARDEV, + "bes2600 factory check failed, calc_crc:0x%08x factory_crc: 0x%08x\n", + cal_crc, factory_data->head.crc); + return -1; + } + return 0; + +#else + return 0; +#endif +} + +/** + * factory_section_read_file - Read data of specified length from file + * @path: path of the file + * @buffer: storage of read data + * + * The maximum file length allowed is 600 bytes. + * This function does not do crc verification to take into + * account different storage requirements. + * + * Return: length on success, negative error code otherwise. + */ +static int factory_section_read_file(char *path, void *buffer) +{ + int ret = 0; + struct file *fp; + + if (!path || !buffer) { + bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__); + return -1; + } + + bes2600_info(BES2600_DBG_FACTORY, "reading %s \n", path); + + fp = filp_open(path, O_RDONLY, 0); //S_IRUSR + if (IS_ERR(fp)) { + bes2600_info(BES2600_DBG_FACTORY, "BES2600 : can't open %s\n",path); + return -1; + } + + if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) { + bes2600_err(BES2600_DBG_FACTORY, + "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n", + fp->f_inode->i_size, FACTORY_MAX_SIZE); + filp_close(fp, NULL); + return -1; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)) + ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos); +#else + ret = kernel_read(fp, fp->f_pos, buffer, fp->f_inode->i_size); +#endif + + filp_close(fp, NULL); + + if (ret != fp->f_inode->i_size) { + bes2600_err(BES2600_DBG_FACTORY, "bes2600_factory.txt read fail\n"); + ret = -1; + } + + return ret; +} + +/** + * factory_section_write_file - Write data of specified length to file + * @path: path of the file + * @buffer: storage of write data + * @size: length of data to write + * + * Return: length on success, negative error code otherwise. + */ +static int factory_section_write_file(char *path, void *buffer, int size) +{ + int ret = 0; + struct file *fp; + + bes2600_info(BES2600_DBG_FACTORY, "writing %s \n", path); + + fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR); + if (IS_ERR(fp)) { + bes2600_info(BES2600_DBG_FACTORY, "BES2600 : can't open %s\n",path); + return -1; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)) + ret = kernel_write(fp, buffer, size, &fp->f_pos); +#else + ret = kernel_write(fp, buffer, size, fp->f_pos); +#endif + + filp_close(fp,NULL); + + return ret; +} + +static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory) +{ + int ret = 0; + + if (!source_buf || !factory) { + bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__); + return -1; + } + + ret = sscanf(source_buf, STANDARD_FACTORY,\ + &factory->head.magic,\ + &factory->head.version,\ + &factory->head.crc,\ + &factory->data.iQ_offset,\ + &factory->data.freq_cal,\ + &factory->data.freq_cal_flags,\ + &factory->data.tx_power_ch[0],\ + &factory->data.tx_power_ch[1],\ + &factory->data.tx_power_ch[2],\ + &factory->data.tx_power_type,\ + &factory->data.temperature,\ + &factory->data.tx_power_ch_5G[0],\ + &factory->data.tx_power_ch_5G[1],\ + &factory->data.tx_power_ch_5G[2],\ + &factory->data.tx_power_ch_5G[3],\ + &factory->data.tx_power_ch_5G[4],\ + &factory->data.tx_power_ch_5G[5],\ + &factory->data.tx_power_ch_5G[6],\ + &factory->data.tx_power_ch_5G[7],\ + &factory->data.tx_power_ch_5G[8],\ + &factory->data.tx_power_ch_5G[9],\ + &factory->data.tx_power_ch_5G[10],\ + &factory->data.tx_power_ch_5G[11],\ + &factory->data.tx_power_ch_5G[12],\ + &factory->data.tx_power_flags_5G,\ + &factory->data.temperature_5G,\ + &factory->data.bt_tx_power[0],\ + &factory->data.bt_tx_power[1],\ + &factory->data.bt_tx_power[2],\ + &factory->data.bt_tx_power[3]); + + if (ret != 30) { + bes2600_err(BES2600_DBG_FACTORY, "bes2600_factory.txt parse fail\n"); + return -1; + } + + return ret; +} + +static int factory_section_read_and_check_file(u8 *file_buf, char* path) +{ + int ret = 0; + + if (!file_buf || !path) { + bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__); + return -1; + } + + ret = factory_section_read_file(path, file_buf); + if (ret < 0) + return ret; + + memset(&factory_cali_data, 0, sizeof(struct factory_t)); + ret = factory_parse(file_buf, &factory_cali_data); + if (ret < 0) + return ret; + + ret = bes2600_factory_head_info_check(&factory_cali_data); + if (ret < 0) + return ret; + + ret = bes2600_factory_crc_check(&factory_cali_data); + if (ret < 0) + return ret; + + factory_p = &factory_cali_data; + + bes2600_info(BES2600_DBG_FACTORY, "open wifi factory section success"); + + return 0; +} + +static void factory_section_wifi_tx_power_check(struct factory_t *factory_data) +{ + int i; + bool inval_v = false; + + if (!factory_data) + return ; + + /* only check cali channel, 11n ch1, ch7, ch13 */ + for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch); ++i) { + if (factory_data->data.tx_power_ch[i] == 0x0 || + factory_data->data.tx_power_ch[i] > 0x3fff) { + inval_v = true; + } + } + + bes2600_warn_with_cond(inval_v, BES2600_DBG_FACTORY, "tx_power_ch_2g cali, inval calibration value\n"); + bes2600_info_dump(BES2600_DBG_FACTORY, "tx_power_ch_2g dump:", factory_data->data.tx_power_ch, + sizeof(factory_data->data.tx_power_ch)); + +} + + +static void factory_section_wifi_tx_power_5G_check(struct factory_t *factory_data) +{ + int i; + bool inval_v = false; + + if (!factory_data) + return ; + + /* only check cali channel */ + for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch_5G); ++i) { + if (factory_data->data.tx_power_ch_5G[i] == 0x0 || + factory_data->data.tx_power_ch_5G[i] > 0x3fff) { + inval_v = true; + } + } + + bes2600_warn_with_cond(inval_v, BES2600_DBG_FACTORY, "tx_power_ch_5g cali, inval calibration value\n"); + bes2600_info_dump(BES2600_DBG_FACTORY, "tx_power_ch_5g dump:", factory_data->data.tx_power_ch_5G, + sizeof(factory_data->data.tx_power_ch_5G)); + +} + +static void factory_section_wifi_freq_cali_check(struct factory_t *factory_data) +{ + if (!factory_data) + return ; + + if (factory_data->data.freq_cal == 0x0 || + factory_data->data.freq_cal > 0x1ff) { + bes2600_warn(BES2600_DBG_FACTORY, "freq cali, inval calibration value\n"); + } + + bes2600_info_dump(BES2600_DBG_FACTORY, "wifi freq cali dump:", &factory_data->data.freq_cal, + sizeof(factory_data->data.freq_cal)); + +} + +static void factory_section_bt_tx_power_check(struct factory_t *factory_data) +{ + int i; + bool inval_v = false; + + if (!factory_data) + return ; + + /* bt only check bdr & edr power, (bdr/edr: div, powerlevel) */ + for (i = 0; i < ARRAY_SIZE(factory_data->data.bt_tx_power) - 1; i += 2) { + if (factory_data->data.bt_tx_power[i] != 0x05) { + inval_v = true; + } + + if (factory_data->data.bt_tx_power[i + 1] == 0x0 || + factory_data->data.bt_tx_power[i + 1] > 0x20) { + inval_v = true; + } + } + + bes2600_warn_with_cond(inval_v, BES2600_DBG_FACTORY, "bt tx power cali, inval calibration value\n"); + bes2600_info_dump(BES2600_DBG_FACTORY, "bt tx power cali dump:", factory_data->data.bt_tx_power, + sizeof(factory_data->data.bt_tx_power)); + +} + +void factory_little_endian_cvrt(u8 *data) +{ + int i; + struct factory_t *trans_data = NULL; + if (!data) + return ; + + trans_data = (struct factory_t *)data; + + TRANS_LE16(trans_data->head.magic); + TRANS_LE16(trans_data->head.version); + TRANS_LE32(trans_data->head.crc); + + TRANS_LE16(trans_data->data.freq_cal); + TRANS_LE32(trans_data->data.iQ_offset); + + for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch); i++) + TRANS_LE16(trans_data->data.tx_power_ch[i]); + + TRANS_LE16(trans_data->data.temperature); + + for (i = 0; i < ARRAY_SIZE(trans_data->data.bt_tx_power); i++) + TRANS_LE32(trans_data->data.bt_tx_power[i]); + + for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch_5G); i++) + TRANS_LE16(trans_data->data.tx_power_ch_5G[i]); + + TRANS_LE16(trans_data->data.tx_power_flags_5G); + TRANS_LE16(trans_data->data.temperature_5G); + +} + +void bes2600_factory_data_check(u8* data) +{ + struct factory_t *factory_data = NULL; + + if (!data) + return ; + + factory_data = (struct factory_t *)data; + + factory_section_wifi_tx_power_check(factory_data); + factory_section_wifi_tx_power_5G_check(factory_data); + factory_section_bt_tx_power_check(factory_data); + factory_section_wifi_freq_cali_check(factory_data); + + /* In order to support manual value change, recalculate crc before sending */ + factory_data->head.crc = + factory_crc32((uint8_t *)(&(factory_data->data)), sizeof(factory_data_t)); + +} + +/* + * get factory data from file each time, and update factory_p. + */ +u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path) +{ + u8 *ret_p = NULL; + + if (!file_buffer || !path || !data_len) { + bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer\n", __func__); + return NULL; + } + + /* reset factory_p */ + factory_p = NULL; + + *data_len = sizeof(struct factory_t); + if (factory_section_read_and_check_file(file_buffer, path) < 0) { + bes2600_err(BES2600_DBG_FACTORY, "read and check %s error\n", path); + *data_len = 0; + return NULL; + } + + if (!factory_p) { + *data_len = 0; + return NULL; + } + + ret_p = (u8 *)factory_p; + + return ret_p; +} + +/** + * When the calibration file does not exist, a new file is automatically created when + * the calibration value is written. After writing, update factory_p, if the update is successful, + * it means the writing is successful, otherwise it fails. At the same time, subsequent calibration + * values are saved on this basis to avoid duplicating file creation and flushing out previously saved values. + */ +static bool bes2600_factory_file_status_read(u8 *file_buffer) +{ + u8 *factory_temp = NULL; + uint32_t len; + bool ret = true; + +#ifdef FACTORY_SAVE_MULTI_PATH + factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH); + if (!factory_temp) { + bes2600_warn(BES2600_DBG_FACTORY, "get factory cali from first path fali\n"); + factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_DEFAULT_PATH); + /* clear the flag of the file in the default path, and then create a new file */ + if (factory_temp) { + ((struct factory_t *)factory_temp)->data.tx_power_type = 0xff; + ((struct factory_t *)factory_temp)->data.freq_cal_flags = 0; + ((struct factory_t *)factory_temp)->data.tx_power_flags_5G = 0; + } + } +#else + factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH); +#endif + if (!factory_temp) { + bes2600_warn(BES2600_DBG_FACTORY, "get factory data fali, check whether the file exists\n"); + ret = false; + } + + return ret; +} + +/* create a new factory.txt file, and set default value */ +static int bes2600_factory_cali_file_hdr_fill(struct factory_t **factory_head) +{ + u16 tx_power_type = 0xff; + int i; + + if (!factory_head) + return -1; + + *factory_head = &factory_cali_data; + memset(*factory_head, 0, sizeof(struct factory_t)); + (*factory_head)->data.tx_power_type = tx_power_type; + (*factory_head)->head.magic = 0xba80; + (*factory_head)->head.version = 2; + + (*factory_head)->data.freq_cal = 0xa0; + + for (i = 0; i < 3; ++i) { + (*factory_head)->data.tx_power_ch[i] = 0x1400; + } + + for (i = 0; i < 13; ++i) { + (*factory_head)->data.tx_power_ch_5G[i] = 0x1400; + } + + (*factory_head)->data.bt_tx_power[0] = 0x05; + (*factory_head)->data.bt_tx_power[1] = 0x10; + (*factory_head)->data.bt_tx_power[2] = 0x05; + (*factory_head)->data.bt_tx_power[3] = 0x15; + + return 0; +} + + +int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali) +{ + u16 mode, band, ch, power_cali, bandwidth; + int power_index = 0; + struct factory_t *factory_power_p = NULL; + u8 *file_buffer = NULL; + int16_t ret = 0; + + if (!data_cali) { + bes2600_warn(BES2600_DBG_FACTORY, "%s: power cali save pointer is NULL\n", __func__); + return -FACTORY_GET_INPUT_NULL_POINTER; + } + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -FACTORY_GET_INPUT_NULL_POINTER; + + bes2600_factory_lock(); + + /** + * When it returns true, it means that the factory file has been read. + * When it returns false, it means that the factory file does not exist, + * or the operation of reading the text file fails. At this time, a new factory file will be created. + */ + if (bes2600_factory_file_status_read(file_buffer)) { + factory_power_p = factory_p; + } else { + if (bes2600_factory_cali_file_hdr_fill(&factory_power_p)) { + bes2600_warn(BES2600_DBG_FACTORY, "%s, create bes2600_factory.txt fail.", __func__); + ret = -FACTORY_FACTORY_TXT_CREATE_FAIL; + goto err; + } + } + + mode = data_cali->mode; + bandwidth = data_cali->bandwidth; + band = data_cali->band; + ch = data_cali->ch; + power_cali = data_cali->power_cali; + + bes2600_dbg(BES2600_DBG_FACTORY, "%s: mode = %u, bandwidth = %u,, band = %u, ch = %u, power_cali = 0x%04x\n", + __func__, mode, bandwidth, band, ch, power_cali); + + /* only in 802.11n 20M msc7 mode, the power calibration value is saved */ + if (bandwidth != 0 || mode != WIFI_RF_11N_MODE) { + ret = -FACTORY_SAVE_MODE_ERR; + goto err; + } + + /* powerlevel value range: 0 ~ 0x3fff */ + if (power_cali == 0 || power_cali > 0x3fff) { + ret = -FACTORY_SAVE_POWER_ERR; + goto err; + } + + + if (band == BAND_2G4) { + switch (ch) { + case 1: + power_index = 0; + break; + case 7: + power_index = 1; + break; + case 13: + power_index = 2; + break; + default: + ret = -FACTORY_SAVE_CH_ERR; + goto err; + break; + } + factory_power_p->data.tx_power_ch[power_index] = power_cali; + } else if (band == BAND_5G) { + switch (ch) { + case 36: + case 38: + case 40: + power_index = 0; + break; + case 44: + case 46: + case 48: + power_index = 1; + break; + case 52: + case 54: + case 56: + power_index = 2; + break; + case 60: + case 62: + case 64: + power_index = 3; + break; + case 100: + case 102: + case 104: + power_index = 4; + break; + case 108: + case 110: + case 112: + power_index = 5; + break; + case 116: + case 114: + case 120: + power_index = 6; + break; + case 124: + case 126: + case 128: + power_index = 7; + break; + case 132: + case 134: + case 136: + power_index = 8; + break; + case 140: + case 142: + case 144: + power_index = 9; + break; + case 149: + case 151: + case 153: + power_index = 10; + break; + case 157: + case 159: + case 161: + power_index = 11; + break; + case 165: + case 169: + power_index = 12; + break; + default: + ret = -FACTORY_SAVE_CH_ERR; + goto err; + break; + } + factory_power_p->data.tx_power_ch_5G[power_index] = power_cali; + } + + /* save to file */ + if (bes2600_wifi_cali_table_save(file_buffer, factory_power_p)) { + ret = -FACTORY_SAVE_WRITE_ERR; + goto err; + } + +err: + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + return ret; + +} + +int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali) +{ + u16 freq_cali; + struct factory_t *factory_freq_p = NULL; + u8 *file_buffer = NULL; + int16_t ret = 0; + + if (!data_cali) { + bes2600_warn(BES2600_DBG_FACTORY, "%s: freq cali save pointer is NULL\n", __func__); + return -FACTORY_GET_INPUT_NULL_POINTER; + } + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -FACTORY_GET_INPUT_NULL_POINTER; + + bes2600_factory_lock(); + + /** + * When it returns true, it means that the factory file has been read. + * When it returns false, it means that the factory file does not exist, + * or the operation of reading the text file fails. At this time, a new factory file will be created. + */ + if (bes2600_factory_file_status_read(file_buffer)) { + factory_freq_p = factory_p; + } else { + if (bes2600_factory_cali_file_hdr_fill(&factory_freq_p)) { + bes2600_warn(BES2600_DBG_FACTORY, "%s, create bes2600_factory.txt fail.", __func__); + ret = -FACTORY_FACTORY_TXT_CREATE_FAIL; + goto err; + } + } + + freq_cali = data_cali->freq_cali; + data_cali->cali_flag = 1; + + /* freqOffset value range: 0 ~ 0x1ff */ + if (freq_cali == 0 || freq_cali > 0x1ff) { + ret = -FACTORY_SAVE_FREQ_ERR; + goto err; + } + + factory_freq_p->data.freq_cal = freq_cali; + factory_freq_p->data.freq_cal_flags = (u8)(data_cali->cali_flag); + bes2600_dbg(BES2600_DBG_FACTORY, "%s: freq_cali = 0x%04x\n", __func__, data_cali->freq_cali); + + /* save to file */ + if (bes2600_wifi_cali_table_save(file_buffer, factory_freq_p)) { + ret = -FACTORY_SAVE_WRITE_ERR; + goto err; + } + +err: + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + return ret; +} + +int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag) +{ + struct factory_t *factory_power_flag_p = NULL; + u8 *file_buffer = NULL; + u16 calied_flag_5g = 1; + u16 calied_flag_2g = 0; + int16_t ret = 0; + + if (!cali_flag) { + bes2600_warn(BES2600_DBG_FACTORY, "%s: power cali flag save pointer is NULL\n", __func__); + return -FACTORY_GET_INPUT_NULL_POINTER; + } + + if (cali_flag->band != BAND_2G4 && cali_flag->band != BAND_5G) { + bes2600_warn(BES2600_DBG_FACTORY, "%s: power cali flag save band err\n", __func__); + return -FACTORY_SET_POWER_CALI_FLAG_ERR; + } + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -FACTORY_GET_INPUT_NULL_POINTER; + + bes2600_factory_lock(); + + if (bes2600_factory_file_status_read(file_buffer)) { + factory_power_flag_p = factory_p; + } else { + bes2600_warn(BES2600_DBG_FACTORY, "%s: factory cali data is not exist\n", __func__); + ret = -FACTORY_SAVE_FILE_NOT_EXIST; + goto err; + } + + if (cali_flag->band == BAND_2G4) { + factory_power_flag_p->data.tx_power_type = calied_flag_2g; + } else { + factory_power_flag_p->data.tx_power_flags_5G = calied_flag_5g; + } + + /* save to file */ + if (bes2600_wifi_cali_table_save(file_buffer, factory_power_flag_p)) { + ret = -FACTORY_SET_POWER_CALI_FLAG_ERR; + goto err; + } + +err: + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + return ret; + +} + +static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory) +{ + return snprintf(dest_buf, FACTORY_MAX_SIZE, STANDARD_FACTORY,\ + factory->head.magic,\ + factory->head.version,\ + factory->head.crc,\ + factory->data.iQ_offset,\ + factory->data.freq_cal,\ + factory->data.freq_cal_flags,\ + factory->data.tx_power_ch[0],\ + factory->data.tx_power_ch[1],\ + factory->data.tx_power_ch[2],\ + factory->data.tx_power_type,\ + factory->data.temperature,\ + factory->data.tx_power_ch_5G[0],\ + factory->data.tx_power_ch_5G[1],\ + factory->data.tx_power_ch_5G[2],\ + factory->data.tx_power_ch_5G[3],\ + factory->data.tx_power_ch_5G[4],\ + factory->data.tx_power_ch_5G[5],\ + factory->data.tx_power_ch_5G[6],\ + factory->data.tx_power_ch_5G[7],\ + factory->data.tx_power_ch_5G[8],\ + factory->data.tx_power_ch_5G[9],\ + factory->data.tx_power_ch_5G[10],\ + factory->data.tx_power_ch_5G[11],\ + factory->data.tx_power_ch_5G[12],\ + factory->data.tx_power_flags_5G,\ + factory->data.temperature_5G,\ + factory->data.bt_tx_power[0],\ + factory->data.bt_tx_power[1],\ + factory->data.bt_tx_power[2],\ + factory->data.bt_tx_power[3]); +} + +static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p) +{ + int ret = 0; + int w_size; + + bes2600_info(BES2600_DBG_FACTORY, "enter %s\n", __func__); + + if (!file_buffer) { + return -ENOMEM; + } + + if (!factory_save_p) { + return -ENOENT; + } + + /* All initialized to space */ + memset(file_buffer, 32, FACTORY_MAX_SIZE); + file_buffer[FACTORY_MAX_SIZE - 1] = '\n'; + + factory_save_p->head.crc = + factory_crc32((uint8_t *)(&(factory_save_p->data)), sizeof(factory_data_t)); + + w_size = factory_build(file_buffer, factory_save_p); + + if (w_size < 0 || w_size > FACTORY_MAX_SIZE) { + bes2600_err(BES2600_DBG_FACTORY, "%s: build failed! ret = %d.", __func__, ret); + return -ETXTBSY; + } + +#ifdef FACTORY_SAVE_MULTI_PATH + /* avoid trailing characters '\0' */ + file_buffer[w_size] = 32; + ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE); +#else + ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size); +#endif + if(ret < 0) { + bes2600_err(BES2600_DBG_FACTORY, "%s: write failed! ret = %d.", __func__, ret); + return ret; + } + + return 0; +} + +int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali) +{ + u8 *file_buffer = NULL; + int16_t ret = 0; + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -FACTORY_GET_INPUT_NULL_POINTER; + + bes2600_factory_lock(); + + if (!power_cali) { + ret = -FACTORY_GET_INPUT_NULL_POINTER; + goto err; + } + + if (!bes2600_factory_file_status_read(file_buffer)) { + ret = -FACTORY_SAVE_FILE_NOT_EXIST; + goto err; + } + + memcpy(power_cali->tx_power_ch, factory_p->data.tx_power_ch, sizeof(power_cali->tx_power_ch)); + memcpy(power_cali->tx_power_ch_5G, factory_p->data.tx_power_ch_5G, sizeof(power_cali->tx_power_ch_5G)); + +err: + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + return ret; +} + + +int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq) +{ + u8 *file_buffer = NULL; + int16_t ret = 0; + + if (!vendor_freq) + return -FACTORY_GET_INPUT_NULL_POINTER; + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -FACTORY_GET_INPUT_NULL_POINTER; + + bes2600_factory_lock(); + + if (!bes2600_factory_file_status_read(file_buffer)) { + ret = -FACTORY_SAVE_FILE_NOT_EXIST; + goto err; + } + + vendor_freq->status = 0; + vendor_freq->freq_cali = factory_p->data.freq_cal; + vendor_freq->cali_flag = factory_p->data.freq_cal_flags; + +err: + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + return ret; +} + diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h new file mode 100644 index 000000000000..473840282a82 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_factory.h @@ -0,0 +1,208 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __FACTORY_H__ +#define __FACTORY_H__ + +#include "bes2600.h" +#include "wsm.h" + +#define NVREC_MINI_VERSION 1 +#define NVREC_DEV_MAGIC 0xba80 +#define NVREC_CURRENT_VERSION 2 + +#define FACTORY_MAX_SIZE 600 +#define STANDARD_FACTORY "##head\n\ +magic:0x%hx\n\ +version:0x%hx\n\ +crc:0x%x\n\ +##iq&xtal\n\ +iQ_offset:0x%x\n\ +freq_cal:0x%hx\n\ +freq_cal_flags:0x%hhx\n\ +##2.4g_power_11n\n\ +ch1:0x%hx\n\ +ch7:0x%hx\n\ +ch13:0x%hx\n\ +tx_power_type:0x%hhx\n\ +temperature:0x%hx\n\ +##5g_power_11n\n\ +ch36-40:0x%hx\n\ +ch44-48:0x%hx\n\ +ch52-56:0x%hx\n\ +ch60-64:0x%hx\n\ +ch100-104:0x%hx\n\ +ch108-112:0x%hx\n\ +ch116-120:0x%hx\n\ +ch124-128:0x%hx\n\ +ch132-136:0x%hx\n\ +ch140-144:0x%hx\n\ +ch149-153:0x%hx\n\ +ch157-161:0x%hx\n\ +ch165-169:0x%hx\n\ +tx_power_flags_5G:0x%hx\n\ +temperature_5G:0x%hx\n\ +##bt\n\ +bdr_div:0x%x\n\ +bdr_power:0x%x\n\ +edr_div:0x%x\n\ +edr_power:0x%x\n\ +%%%%\n" + +typedef struct { + uint16_t magic; + uint16_t version; + uint32_t crc; +} factory_head_t; + +typedef struct { + uint32_t iQ_offset; + uint16_t freq_cal; + /** + * index 0-2 + * 11n ch1, 11n ch7, 11n ch13 + */ + uint16_t tx_power_ch[3]; + + /** + * freq_cal_flags 0 - chip not calibrated + * freq_cal_flags 1 - chip has been calibrated + */ + uint8_t freq_cal_flags; + + /** + * tx_power_type 0 - save bgn 1,7,13 power + * tx_power_type 1 - save bgn 1-13 power + * tx_power_type 0xff - not calibration + */ + uint8_t tx_power_type; + uint16_t temperature; + + /** + * 11n + * 0 36~40:1 44~48:2 52~56:3 60~64; + * 4 100~104:5 108~112:6 116~120; + * 7 124~128:8 132~136:9 140~144 + * 10 149~153; 11 157~161:12 165~169 + */ + uint16_t tx_power_ch_5G[13]; + /** + * 0- it means that power not calib + * 1- it means that power have clibrated + */ + uint16_t tx_power_flags_5G; + + + uint32_t bt_tx_power[4]; + /* The temperature after 5G clibrating. */ + uint16_t temperature_5G; +} factory_data_t; + +struct factory_t { + factory_head_t head; + factory_data_t data; +}; + + +enum band_type { + BAND_2G4, + BAND_5G, +}; + +struct wifi_get_power_cali_t { + uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */ + uint16_t tx_power_ch[3]; + uint16_t tx_power_ch_5G[13]; + int16_t status; /* 0: success, != 0: fial */ +}; + +struct wifi_power_cali_save_t { + uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */ + uint16_t mode; + uint16_t bandwidth; + uint16_t band; + uint16_t ch; + uint16_t power_cali; + int16_t status; /* 0: success, != 0: fial */ +}; + +struct wifi_freq_cali_t { + uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */ + uint16_t freq_cali; + int16_t status; /* 0: success, != 0: fial */ + uint16_t cali_flag; +}; + +struct wifi_power_cali_flag_t { + uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */ + uint16_t band; + int16_t status; /* 0: success, != 0: fial */ +}; + + +/** + * fatory cali data save type + * @RF_CALIB_DATA_IN_LINUX - save to linux file + * @RF_CALIB_DATA_IN_EFUSE - save to efuse + * @RF_CALIB_DATA_IN_FLASH - save to flash + * @RF_CALIB_DATA_TYPE_MAX - save type num + */ +enum RF_FACTORY_CALI_DATA_SAVE_TYPE { + RF_CALIB_DATA_IN_LINUX = 0, + RF_CALIB_DATA_IN_EFUSE, + RF_CALIB_DATA_IN_FLASH, + RF_CALIB_DATA_TYPE_MAX, +}; + +/* fatory power & freq cali save status code */ +enum factory_cali_status { + FACTORY_SAVE_SUCCESS = 0, + FACTORY_SAVE_FILE_NOT_EXIST = 1, + FACTORY_SAVE_MODE_ERR = 2, + FACTORY_SAVE_CH_ERR = 3, + FACTORY_SAVE_POWER_ERR = 4, + FACTORY_SAVE_FREQ_ERR = 5, + FACTORY_SAVE_EFUSE_CALIED = 6, + FACTORY_SAVE_WRITE_ERR = 7, + FACTORY_GET_CALIB_FROM_EFUSE_ERR = 8, + FACTORY_GET_FREQ_FROM_EFUSE_ERR = 9, + FACTORY_GET_POWER_FROM_EFUSE_ERR = 10, + FACTORY_GET_POWER_FROM_FLASH_ERR = 11, + FACTORY_GET_FREQ_FROM_FLASH_ERR = 12, + FACTORY_SET_POWER_CALI_FLAG_ERR = 13, + FACTORY_SET_FREQ_CALI_FLAG_ERR = 14, + FACTORY_SAVE_READ_ERR = 15, + FACTORY_GET_INPUT_NULL_POINTER = 16, + FACTORY_FACTORY_TXT_CREATE_FAIL = 17, + /* add new here, and numbered sequentially */ +}; + + + +/* just calibrate 11n, other protocols are automatically mapped */ +#define WIFI_RF_11N_MODE 0x15 + +/* read wifi & bt factory cali value*/ +u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path); +void factory_little_endian_cvrt(u8 *data); +void bes2600_factory_data_check(u8* data); +void bes2600_factory_lock(void); +void bes2600_factory_unlock(void); +u8* bes2600_factory_get_file_buffer(void); +void bes2600_factory_free_file_buffer(u8 *file_buffer); + +/* read & write wifi cali value */ +int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali); +int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali); +int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq); +int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali); +int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag); + +#endif diff --git a/drivers/staging/bes2600/bes2600_log.c b/drivers/staging/bes2600/bes2600_log.c new file mode 100644 index 000000000000..28f6ed1a1ada --- /dev/null +++ b/drivers/staging/bes2600/bes2600_log.c @@ -0,0 +1,152 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#include "bes2600_log.h" +#include "bes2600.h" + +struct bes2600_dbg_info { + int dbg_lvl; + char *module_name; +}; + +struct bes2600_dbg_info bes2600_dbg_tbl[BES2600_DBG_MAX] = { + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "download"}, /* BES2600_DBG_DOWNLOAD */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "niy"}, /* BES2600_DBG_NIY */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "sbus"}, /* BES2600_DBG_SBUS */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "init"}, /* BES2600_DBG_INIT */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "txrx_opt"}, /* BES2600_DBG_TXRX_OPT */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "txrx"}, /* BES2600_DBG_TXRX */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "queue"}, /* BES2600_DBG_QUEUE */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "spi"}, /* BES2600_DBG_SPI */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "sdio"}, /* BES2600_DBG_SDIO */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "usb"}, /* BES2600_DBG_USB */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "pm"}, /* BES2600_DBG_PM */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "sys"}, /* BES2600_DBG_SYS */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "bt"}, /* BES2600_DBG_BT */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "android"}, /* BES2600_DBG_ANDROID */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "bh"}, /* BES2600_DBG_BH */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "ap"}, /* BES2600_DBG_AP */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "sta"}, /* BES2600_DBG_STA */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "scan"}, /* BES2600_DBG_SCAN */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "itp"}, /* BES2600_DBG_ITP */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "test_mode"}, /* BES2600_DBG_TEST_MODE */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "tx_policy"}, /* BES2600_DBG_TX_POLICY */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "wsm"}, /* BES2600_DBG_WSM */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "roc"}, /* BES2600_DBG_ROC */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "chardev"}, /* BES2600_DBG_CHARDEV */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "factory"}, /* BES2600_DBG_FACTORY */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "epta"}, /* BES2600_DBG_EPTA */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "bes_pwr"}, /* BES2600_DBG_PWR */ + {.dbg_lvl = BES2600_LOG_INFO, .module_name = "tx_loop"}, /* BES2600_DBG_TXLOOP */ +}; + +int bes2600_get_dbg_lvl(int module) +{ + if(module >= ARRAY_SIZE(bes2600_dbg_tbl)) + return BES2600_LOG_NONE; + + return bes2600_dbg_tbl[module].dbg_lvl; +} + +int bes2600_log_control_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +ssize_t bes2600_log_control_write(struct file *file,const char __user *user_buf, size_t count, loff_t *ppos) +{ + u32 i; + char *buf = NULL; + char *module, *level; + unsigned long log_level; + char *str_none = "NONE"; + char *str_err = "ERR"; + char *str_warn = "WARN"; + char *str_info = "INFO"; + char *str_dbg = "DBG"; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + buf[count] = '\0'; + + module = strstr(buf, "module:"); + level = strstr(buf, "level:"); + if (module == NULL || level == NULL) { + bes2600_err(BES2600_DBG_CHARDEV, "module or level error. %s\n", buf); + goto out; + } + module += 7; + level += 6; + if (strncasecmp(str_none, level, strlen(str_none)) == 0) + log_level = BES2600_LOG_NONE; + else if(strncasecmp(str_err, level, strlen(str_err)) == 0) + log_level = BES2600_LOG_ERROR; + else if(strncasecmp(str_warn, level, strlen(str_warn)) == 0) + log_level = BES2600_LOG_WARN; + else if(strncasecmp(str_info, level, strlen(str_info)) == 0) + log_level = BES2600_LOG_INFO; + else if(strncasecmp(str_dbg, level, strlen(str_dbg)) == 0) + log_level = BES2600_LOG_DBG; + else { + bes2600_err(BES2600_DBG_CHARDEV, "module level error. %s\n", __func__); + goto out; + } + + for (i = 0; i < ARRAY_SIZE(bes2600_dbg_tbl); ++i) { + if (strncasecmp(bes2600_dbg_tbl[i].module_name, module, strlen(bes2600_dbg_tbl[i].module_name)) == 0) { + bes2600_dbg_tbl[i].dbg_lvl = log_level; + break; + } + } + +out: + kfree(buf); + return count; +} + +ssize_t bes2600_log_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buf = NULL; + int i,ret; + int used_len = 0; + char str[10]; + + if (!count) + return -EINVAL; + buf = kmalloc(1024, GFP_KERNEL); + if(!buf) { + bes2600_err(BES2600_DBG_CHARDEV, "kmalloc error. %s\n", __func__); + return -ENOMEM; + } + for (i = 0; i < ARRAY_SIZE(bes2600_dbg_tbl); ++i) { + if(bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_NONE) + strcpy(str, "NONE"); + else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_ERROR) + strcpy(str, "ERR"); + else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_WARN) + strcpy(str, "WARN"); + else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_INFO) + strcpy(str, "INFO"); + else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_DBG) + strcpy(str, "DBG"); + else + return -ENOMEM; + used_len += snprintf(buf + used_len, (1024 - used_len), "%-16s%s\n", bes2600_dbg_tbl[i].module_name, str); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, used_len); + kfree(buf); + return ret; +} diff --git a/drivers/staging/bes2600/bes2600_log.h b/drivers/staging/bes2600/bes2600_log.h new file mode 100644 index 000000000000..14f36810b072 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_log.h @@ -0,0 +1,161 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __BES2600_LOG_H__ +#define __BES2600_LOG_H__ + +/* Log Level Control */ +#define BES2600_LOG_NONE 0 +#define BES2600_LOG_ERROR 1 +#define BES2600_LOG_WARN 2 +#define BES2600_LOG_INFO 3 +#define BES2600_LOG_DBG 4 + +/* Module Log Level Control */ +enum BES2600_DBG { + BES2600_DBG_DOWNLOAD = 0, + BES2600_DBG_NIY, + BES2600_DBG_SBUS, + BES2600_DBG_INIT, + BES2600_DBG_TXRX_OPT, + BES2600_DBG_TXRX, + BES2600_DBG_QUEUE, + BES2600_DBG_SPI, + BES2600_DBG_SDIO, + BES2600_DBG_USB, + BES2600_DBG_PM, + BES2600_DBG_SYS, + BES2600_DBG_BT, + BES2600_DBG_ANDROID, + BES2600_DBG_BH, + BES2600_DBG_AP, + BES2600_DBG_STA, + BES2600_DBG_SCAN, + BES2600_DBG_ITP, + BES2600_DBG_TEST_MODE, + BES2600_DBG_TX_POLICY, + BES2600_DBG_WSM, + BES2600_DBG_ROC, + BES2600_DBG_CHARDEV, + BES2600_DBG_FACTORY, + BES2600_DBG_EPTA, + BES2600_DBG_PWR, + BES2600_DBG_TXLOOP, + + BES2600_DBG_MAX, +}; + +int bes2600_get_dbg_lvl(int module); + +int bes2600_log_control_open(struct inode *inode, struct file *file); + +ssize_t bes2600_log_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos); + +ssize_t bes2600_log_control_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos); + +#define GET_LOG_LVL(module) bes2600_get_dbg_lvl(module) + +#define bes2600_dbg(module, ...) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_DBG) \ + printk(KERN_INFO __VA_ARGS__); \ + } while (0) + +#define bes2600_dbg_with_cond(cond, module, ...) \ + do { \ + if ((0 != (cond)) && \ + GET_LOG_LVL(module) >= BES2600_LOG_DBG) \ + printk(KERN_DEBUG __VA_ARGS__); \ + } while (0) + +#define bes2600_dbg_dump(module, desc, array, len) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_DBG) \ + print_hex_dump(KERN_DEBUG, \ + desc, DUMP_PREFIX_NONE, \ + 16, 1, array, \ + len, false); \ + } while(0) + +#define bes2600_info(module, ...) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_INFO) \ + printk(KERN_INFO __VA_ARGS__); \ + } while (0) + +#define bes2600_info_with_cond(cond, module, ...) \ + do { \ + if ((0 != (cond)) && \ + GET_LOG_LVL(module) >= BES2600_LOG_INFO) \ + printk(KERN_INFO __VA_ARGS__); \ + } while (0) + +#define bes2600_info_dump(module, desc, array, len) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_INFO) \ + print_hex_dump(KERN_INFO, \ + desc, DUMP_PREFIX_NONE, \ + 16, 1, array, \ + len, false); \ + } while(0) + + +#define bes2600_warn(module, ...) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_WARN) \ + printk(KERN_WARNING __VA_ARGS__); \ + } while (0) + +#define bes2600_warn_with_cond(cond, module, ...) \ + do { \ + if ((0 != (cond)) && \ + GET_LOG_LVL(module) >= BES2600_LOG_WARN) \ + printk(KERN_WARNING __VA_ARGS__); \ + } while (0) + +#define bes2600_warn_dump(module, desc, array, len) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_WARN) \ + print_hex_dump(KERN_WARNING, \ + desc, DUMP_PREFIX_NONE, \ + 16, 1, array, \ + len, false); \ + } while(0) + +#define bes2600_err(module, ...) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_ERROR) \ + printk(KERN_ERR __VA_ARGS__); \ + } while (0) + +#define bes2600_err_with_cond(cond, module, ...) \ + do { \ + if ((0 != (cond)) && \ + GET_LOG_LVL(module) >= BES2600_LOG_ERROR) \ + printk(KERN_ERR __VA_ARGS__); \ + } while (0) + +#define bes2600_err_dump(module, desc, array, len) \ + do { \ + if (GET_LOG_LVL(module) >= BES2600_LOG_ERROR) \ + print_hex_dump(KERN_ERR, \ + desc, DUMP_PREFIX_NONE, \ + 16, 1, array, \ + len, false); \ + } while(0) + +#define STUB() \ + do { \ + bes2600_dbg(BES2600_DBG_NIY, \ + "%s: STUB at line %d.\n", \ + __func__, __LINE__); \ + } while (0) + +#endif diff --git a/drivers/staging/bes2600/bes2600_plat.h b/drivers/staging/bes2600/bes2600_plat.h new file mode 100644 index 000000000000..5838435b9315 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_plat.h @@ -0,0 +1,51 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef BES2600_PLAT_H_INCLUDED +#define BES2600_PLAT_H_INCLUDED + +#include + +struct bes2600_platform_data_spi { + u8 spi_bits_per_word; /* REQUIRED */ + + /* All others are optional */ + bool have_5ghz; + int reset; /* GPIO to RSTn signal (0 disables) */ + int powerup; /* GPIO to POWERUP signal (0 disables) */ + int irq_gpio; /* GPIO IRQ */ + int host_wakeup_wlan; /* GPIO to wakeup wlan */ + const u8 *macaddr; /* if NULL, use bes2600_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ + void *priv; +}; + +struct bes2600_platform_data_sdio { + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + bool have_5ghz; + bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ + int reset; /* GPIO to RSTn signal (0 disables) */ + int powerup; /* GPIO to POWERUP signal (0 disables) */ + int wakeup; /* GPIO to WAKEUP signal (0 disables) */ + int host_wakeup; /* wifi GPIO to WAKEUP host signal (0 disables) */ + bool wlan_bt_hostwake_registered;/* wifi request_irq success or not */ + int gpio_irq; /* IRQ line or 0 to use SDIO IRQ */ + int (*power_ctrl)(const struct bes2600_platform_data_sdio *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct bes2600_platform_data_sdio *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use bes2600_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ + bool wakeup_source; /* marks whether bes2600 is the wakeup souce or not */ +}; + +#endif /* BES2600_PLAT_H_INCLUDED */ diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c new file mode 100644 index 000000000000..5252951cc3bb --- /dev/null +++ b/drivers/staging/bes2600/bes2600_sdio.c @@ -0,0 +1,2563 @@ +/* + * Mac80211 SDIO driver for BES2600 device + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #define DEBUG 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bes2600.h" +#include "sbus.h" +#include "bes2600_plat.h" +#include "hwio.h" +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" + +#ifdef PLAT_ROCKCHIP +#include +#endif + +#ifdef PLAT_ALLWINNER +#include + +extern int sunxi_wlan_get_bus_index(void); +extern void sunxi_mmc_rescan_card(unsigned id); +extern void sw_mci_rescan_card(unsigned id, unsigned insert); +extern void sunxi_wlan_set_power(bool on_off); +#endif + +#if defined(BES2600_BOOT_UART_TO_SDIO) +static struct sbus_ops bes2600_sdio_sbus_ops; +extern int bes2600_boot_uart_to_sdio(struct sbus_ops *ops); +#endif +static void sdio_scan_work(struct work_struct *work); +static void bes2600_sdio_power_down(struct sbus_priv *self); +struct bes2600_platform_data_sdio *bes2600_get_platform_data(void); +int bes2600_register_net_dev(struct sbus_priv *bus_priv); +int bes2600_unregister_net_dev(struct sbus_priv *bus_priv); +static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int falg); +static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int falg); + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("mac80211 BES2600 SDIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("bes2600_wlan"); + +struct sbus_priv { + struct sdio_func *func; + struct bes2600_common *core; + const struct bes2600_platform_data_sdio *pdata; + spinlock_t lock; + sbus_irq_handler irq_handler; + void *irq_priv; + struct work_struct sdio_scan_work; + struct device *dev; + struct workqueue_struct *sdio_wq; + bool fw_started; + struct mutex io_mutex; + long unsigned int gpio_wakup_flags; + struct mutex sbus_mutex; + bool retune_protected; +#ifdef BES_SDIO_RXTX_TOGGLE + u8 next_toggle; + int tx_data_toggle; + int rx_data_toggle; +#endif +#ifdef BES_SDIO_RX_MULTIPLE_ENABLE + spinlock_t rx_queue_lock; + struct sk_buff_head rx_queue; + u8 *rx_buffer; + struct work_struct rx_work; + u32 rx_last_ctrl; + u32 rx_valid_ctrl; + u32 rx_total_ctrl_cnt; + u32 rx_continuous_ctrl_cnt; + u32 rx_zero_ctrl_cnt; + u32 rx_remain_ctrl_cnt; + u32 rx_data_cnt; + u32 rx_xfer_cnt; + u32 rx_proc_cnt; + long unsigned int last_irq_timestamp; + long unsigned int last_rx_data_timestamp; +#endif +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + u8 *tx_buffer; + struct list_head tx_bufferlist; + struct kmem_cache *tx_bufferlistpool; + spinlock_t tx_bufferlock; + struct work_struct tx_work; + struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1]; + struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1]; + u32 tx_data_cnt; + u32 tx_xfer_cnt; + u32 tx_proc_cnt; + long unsigned int last_tx_data_timestamp; +#endif +#ifndef SDIO_HOST_ADMA_SUPPORT + u8 *single_gathered_buffer; +#endif + bool unregister_in_process; +}; + +#define IS_DRIVER_VENDOR_CMD(X) ((X & 0x0C00) == 0x0C00) +struct HI_MSG_HDR { + uint16_t MsgLen; + uint16_t MsgId; +}; + +enum DRIVER_TO_MCU_MSG_ST { + ST_ENTER, + ST_EXIT, +}; + +#define BES_VENDOR_ID 0xbe57 +#define BES_DEVICE_ID_2002 0x2002 + +static const struct sdio_device_id bes2600_sdio_ids[] = { + { SDIO_DEVICE(BES_VENDOR_ID, BES_DEVICE_ID_2002) }, + { /* end: all zeroes */ }, +}; + +#ifdef BES2600_GPIO_WAKEUP_AP +#ifdef PLAT_ALLWINNER +extern int sunxi_wlan_get_oob_irq_flags(void); +extern int sunxi_wlan_get_oob_irq(void); +#endif +static int bes2600_gpio_wakeup_ap_config(struct sbus_priv *priv); +#endif + +/* sbus_ops implemetation */ + +#ifdef CONFIG_BES2600_WLAN_BES +static inline unsigned int sdio_max_byte_size(struct sdio_func *func) +{ + unsigned mval = min(func->card->host->max_seg_size, func->card->host->max_blk_size); + + if (func->card->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE) + mval = min(mval, func->cur_blksize); + else + mval = min(mval, func->max_blksize); + + return min(mval, 512u); +} + +static int bes_sdio_memcpy_io_helper(struct sdio_func *func, int write, void *data_buf, unsigned size) +{ + int ret = 0; + unsigned remainder = size; + unsigned max_blocks, align_blocks, pads; + +#ifdef BES_SDIO_RXTX_TOGGLE + struct sbus_priv *self = NULL; +#endif + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg[2]; + +#ifdef SDIO_HOST_ADMA_SUPPORT + struct scatterlist *next; +#endif + +#ifdef SDIO_HOST_ADMA_SUPPORT + u8 *pad_buf = (u8 *)kmalloc(func->cur_blksize, GFP_KERNEL); + if (!pad_buf) + return -ENOMEM; +#endif + + if (!func || (func->num > 7) || (!data_buf) || (!size)) { + ret = -EINVAL; + goto out; + } + +#ifdef BES_SDIO_RXTX_TOGGLE + self = sdio_get_drvdata(func); + BUG_ON(!self); +#endif + + if (func->card->cccr.multi_block && size > sdio_max_byte_size(func) ) { + max_blocks = min(func->card->host->max_blk_count, 511u); + align_blocks = (size + func->cur_blksize - 1) / func->cur_blksize; + if (align_blocks > max_blocks) { + /* to be simplified, consider this should not + * happen, and to be continued; + */ + bes2600_dbg(BES2600_DBG_SDIO, "%s warning to be continued, align=%d max=%d", __func__, align_blocks, max_blocks); + ret = -EINVAL; + goto out; + } + pads = align_blocks * func->cur_blksize - size; + bes2600_dbg(BES2600_DBG_SDIO, "%s sz=%u blk=%u pad=%u,dir=%d", __func__, size, align_blocks, pads, write); + + memset(&mrq, 0, sizeof(mrq)); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_IO_RW_EXTENDED; + cmd.arg = write ? 0x80000000 : 0x00000000; + cmd.arg |= func->num << 28; + cmd.arg |= 0x04000000; + cmd.arg |= size << 9; + +#ifdef BES_SDIO_RXTX_TOGGLE + if (likely(self->fw_started == true)) { + cmd.arg &= ~(1 << 25); + if (write) { + cmd.arg |= ((self->tx_data_toggle & 0x1) << 25); + ++self->tx_data_toggle; + } else { + cmd.arg |= ((self->rx_data_toggle & 0x1) << 25); + ++self->rx_data_toggle; + } + } +#endif + + cmd.arg |= (0x08000000 | align_blocks); + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + data.blksz = func->cur_blksize; + data.blocks = align_blocks; + data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + data.sg = sg; + data.sg_len = 1; +#ifdef SDIO_HOST_ADMA_SUPPORT + sg_init_table(sg, 2); + sg_set_buf(sg, data_buf, size); + if (pads) { + next = sg_next(sg); + sg_set_buf(next, pad_buf, pads); + data.sg_len = 2; + } +#else + sg_init_table(sg, 1); + if (unlikely(pads)) { + if (unlikely(size > MAX_SDIO_TRANSFER_LEN)) { + WARN_ON(1); + return -EINVAL; + } + if (write) + memcpy(self->single_gathered_buffer, data_buf, size); + sg_set_buf(sg, self->single_gathered_buffer, size + pads); + } else { + sg_set_buf(sg, data_buf, size); + } +#endif + + mmc_set_data_timeout(&data, func->card); + + mmc_wait_for_req(func->card->host, &mrq); + + if (cmd.error){ + ret = cmd.error; + goto out; + } + if (data.error) { + ret = data.error; + goto out; + } + if (cmd.resp[0] & R5_ERROR) { + ret = -EIO; + goto out; + } + if (cmd.resp[0] & R5_FUNCTION_NUMBER) { + ret = -EINVAL; + goto out; + } + if (cmd.resp[0] & R5_OUT_OF_RANGE) { + ret = -ERANGE; + goto out; + } + +#ifndef SDIO_HOST_ADMA_SUPPORT + if (pads && (!write)) { + memcpy(data_buf, self->single_gathered_buffer, size); + } +#endif + + } else { + while (remainder) { + size = min(remainder, sdio_max_byte_size(func)); + + bes2600_dbg(BES2600_DBG_SDIO, "%s size=%d dir=%d", __func__, size, write); + if (write) { +#ifndef BES_SDIO_RXTX_TOGGLE + ret = sdio_memcpy_toio(func, size, data_buf, size); +#else + if (likely(self->fw_started == true)) { + ret = sdio_memcpy_toio(func, size | ((self->tx_data_toggle & 0x1) << 16), data_buf, size); + ++self->tx_data_toggle; + } else { + ret = sdio_memcpy_toio(func, size, data_buf, size); + } +#endif + } else { +#ifndef BES_SDIO_RXTX_TOGGLE + ret = sdio_memcpy_fromio(func, data_buf, size, size); +#else + if (likely(self->fw_started == true)) { + ret = sdio_memcpy_fromio(func, data_buf, size | ((self->rx_data_toggle & 0x1) << 16), size); + ++self->rx_data_toggle; + } else { + ret = sdio_memcpy_fromio(func, data_buf, size, size); + } +#endif + } + if (ret) + goto out; + + remainder -= size; + data_buf += size; + } + } +out: +#ifdef SDIO_HOST_ADMA_SUPPORT + kfree(pad_buf); +#endif + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "%s, err=%d(%d:%p:%d)", + __func__, ret, func->num, data_buf, size); +#ifdef BES_SDIO_RXTX_TOGGLE + if (self && self->fw_started == true) { + if (write) + --self->tx_data_toggle; + else + --self->rx_data_toggle; + bes2600_err(BES2600_DBG_SDIO, "%s,toggle count:%u,%u\n", __func__, self->tx_data_toggle, self->rx_data_toggle); + } +#endif + } + return ret; +} +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,19,56)) +static void sdio_retune_hold_now(struct sdio_func *func) +{ + func->card->host->retune_now = 0; + func->card->host->hold_retune += 1; +} + +static void sdio_retune_release(struct sdio_func *func) +{ + if (func->card->host->hold_retune) + func->card->host->hold_retune -= 1; + else + WARN_ON(1); +} +#endif + +static int bes2600_sdio_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + return bes_sdio_memcpy_io_helper(self->func, 0, dst, count); +} + +static int bes2600_sdio_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return bes_sdio_memcpy_io_helper(self->func, 1, (void *)src, count); +} + +static void bes2600_sdio_lock(struct sbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void bes2600_sdio_unlock(struct sbus_priv *self) +{ + sdio_release_host(self->func); +} + +/* bes sdio slave regs can only be accessed by command52 + * if a WORD or DWORD reg wants to be accessed, + * please combine the results of multiple command52 + */ +static int bes2600_sdio_reg_read(struct sbus_priv *self, u32 reg, + void *dst, int count) +{ + int ret = 0; + if (count <= 0 || !dst) + return -EINVAL; + while(count && !ret) { + *(u8 *)dst = sdio_readb(self->func, reg, &ret); + dst ++; + reg ++; + count--; + } + return ret; + +} + +static int bes2600_sdio_reg_write(struct sbus_priv *self, u32 reg, + const void *src, int count) +{ + int ret = 0; + if (count <= 0 || !src) + return -EINVAL; + while (count && !ret) { + sdio_writeb(self->func, *(u8 *)src, reg, &ret); + src ++; + reg ++; + count --; + } + return ret; +} + +#ifndef CONFIG_BES2600_USE_GPIO_IRQ +static void bes2600_sdio_irq_handler(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + unsigned long flags; + bes2600_dbg(BES2600_DBG_SDIO, "\n %s called, fw_started:%d \n", + __func__, self->fw_started); + + if (WARN_ON(!self)) { + return; + } + + if (likely(self->fw_started && self->core)) { + queue_work(self->sdio_wq, &self->rx_work); + self->last_irq_timestamp = jiffies; + } else if(self->irq_handler) { + spin_lock_irqsave(&self->lock, flags); + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); + } +} +#else /* CONFIG_BES2600_USE_GPIO_IRQ */ +static u32 bes2600_gpio_irq_handler(void *dev_id) +{ + struct sbus_priv *self = (struct sbus_priv *)dev_id; + + bes2600_dbg(BES2600_DBG_SDIO, "\n %s called \n", __func__); + BUG_ON(!self); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + return 0; +} + +static int bes2600_request_irq(struct sbus_priv *self, + u32 handler) +{ + int ret = 0; + int func_num; + const struct resource *irq = self->pdata->irq; + u8 cccr; + int ret0 = 0; + +#ifdef PLAT_ALLWINNER_SUN6I // for Allwinner we define plat specific API to allocate IRQ line + aw_gpio_irq_handle = sw_gpio_irq_request(irq->start, TRIG_EDGE_POSITIVE, (peint_handle)handler, self); + if (aw_gpio_irq_handle == 0) { + bes2600_err(BES2600_DBG_SDIO, "[%s] err sw_gpio_irq_request.. :%d\n", __func__,aw_gpio_irq_handle); + return -1; + } else + { + ret = 0; + } +#endif // PLAT_ALLWINNER_SUN6I + + /* Hack to access Fuction-0 */ + func_num = self->func->num; + self->func->num = 0; + + cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto set_func; + + /* Master interrupt enable ... */ + cccr |= BIT(0); + + /* ... for our function */ + cccr |= BIT(func_num); + + sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto set_func; + + /* Restore the WLAN function number */ + self->func->num = func_num; + return 0; + +set_func: + //AW judge sdio read write timeout, 1s + ret0 = sw_mci_check_r1_ready(self->func->card->host, 1000); + if (ret0 != 0) + bes2600_err(BES2600_DBG_SDIO, ("%s data timeout.\n", __FUNCTION__)); + + self->func->num = func_num; +#ifdef PLAT_ALLWINNER_SUN6I + sw_gpio_irq_free(aw_gpio_irq_handle); + aw_gpio_irq_handle = 0; +#endif + bes2600_err(BES2600_DBG_SDIO, "[%s] fail exiting sw_gpio_irq_request.. :%d\n",__func__, ret); + return ret; +} +#endif /* CONFIG_BES2600_USE_GPIO_IRQ */ + +static int bes2600_sdio_irq_subscribe(struct sbus_priv *self, + sbus_irq_handler handler, + void *priv) +{ + int ret; + unsigned long flags; + + if (!handler) + return -EINVAL; + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = priv; + self->irq_handler = handler; + spin_unlock_irqrestore(&self->lock, flags); + + bes2600_dbg(BES2600_DBG_SDIO, "SW IRQ subscribe\n"); + sdio_claim_host(self->func); +#ifndef CONFIG_BES2600_USE_GPIO_IRQ + ret = sdio_claim_irq(self->func, bes2600_sdio_irq_handler); +#else + mdelay(10); + ret = bes2600_request_irq(self, bes2600_gpio_irq_handler); +#endif + sdio_release_host(self->func); + return ret; +} + +static int bes2600_sdio_irq_unsubscribe(struct sbus_priv *self) +{ + int ret = 0; + unsigned long flags; +#ifdef CONFIG_BES2600_USE_GPIO_IRQ + const struct resource *irq = self->pdata->irq; +#ifdef PLAT_ALLWINNER_SUN6I + sw_gpio_irq_free(aw_gpio_irq_handle); + aw_gpio_irq_handle = 0; +#endif +#endif + + WARN_ON(!self->irq_handler); + if (!self->irq_handler) + return 0; + + bes2600_dbg(BES2600_DBG_SDIO, "SW IRQ unsubscribe\n"); + +/* +#ifndef CONFIG_BES2600_USE_GPIO_IRQ + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); +#else + free_irq(irq->start, self); +#endif //CONFIG_BES2600_USE_GPIO_IRQ +*/ + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = NULL; + self->irq_handler = NULL; + spin_unlock_irqrestore(&self->lock, flags); + + return ret; +} + +static int bes2600_sdio_off(const struct bes2600_platform_data_sdio *pdata) +{ + bes2600_info(BES2600_DBG_SDIO, "%s enter\n", __func__); + +#if defined(PLAT_ALLWINNER) + sunxi_wlan_set_power(false); +#endif + +#if defined(PLAT_ROCKCHIP) + rockchip_wifi_set_carddetect(0); + rockchip_wifi_power(0); +#endif + + if (pdata == NULL) + return 0; + +#ifdef BES2600_INDEPENDENT_EVB + if (gpio_is_valid(pdata->powerup)) { + gpio_direction_output(pdata->powerup, 0); + } +#endif + + return 0; +} + +static int bes2600_sdio_on(const struct bes2600_platform_data_sdio *pdata) +{ + + bes2600_info(BES2600_DBG_SDIO, "%s enter\n", __func__); + +#if defined(PLAT_ALLWINNER) + sunxi_wlan_set_power(true); +#endif + +#ifdef PLAT_ROCKCHIP + rockchip_wifi_power(0); + rockchip_wifi_power(1); + bes2600_chrdev_start_bus_probe(); + rockchip_wifi_set_carddetect(1); +#endif + + if (pdata != NULL) { +#ifdef BES2600_INDEPENDENT_EVB + if (gpio_is_valid(pdata->powerup)) { + gpio_direction_output(pdata->powerup, 1); + } +#endif + } + +#if defined(BES2600_BOOT_UART_TO_SDIO) + return bes2600_boot_uart_to_sdio(&bes2600_sdio_sbus_ops); +#endif + + return 0; +} + +static size_t bes2600_sdio_align_size(struct sbus_priv *self, size_t size) +{ + size_t aligned = size; + if (self->func->cur_blksize > size) + aligned = sdio_align_size(self->func, size); + else + aligned = (aligned + 3) & (~3); + + return aligned; +} + +int bes2600_sdio_set_block_size(struct sbus_priv *self, size_t size) +{ + return sdio_set_block_size(self->func, size); +} + +void sdio_work_debug(struct sbus_priv *self) +{ + u8 cfg; + int ret; + bes2600_err(BES2600_DBG_SDIO, "%s now=%u last irq timestamp=%u\n", __func__, + (u32)jiffies_to_msecs(jiffies), jiffies_to_msecs(self->last_irq_timestamp)); + bes2600_err(BES2600_DBG_SDIO, "%s rx ctrl: total=%u continuous=%u xfer=%u remain=%u zero=%u last=%x(%x) next=%d\n", __func__, + self->rx_total_ctrl_cnt, self->rx_continuous_ctrl_cnt, self->rx_xfer_cnt, self->rx_remain_ctrl_cnt, self->rx_zero_ctrl_cnt, + self->rx_last_ctrl, self->rx_valid_ctrl, self->next_toggle); + bes2600_err(BES2600_DBG_SDIO, "%s rx: last timestamp=%u, total=%u(%u), proc=%u\n", __func__, + (u32)jiffies_to_msecs(self->last_rx_data_timestamp), + self->rx_data_cnt, self->rx_xfer_cnt, self->rx_proc_cnt); + bes2600_err(BES2600_DBG_SDIO, "%s tx: last timestamp=%u, total=%u,%u, proc=%u\n", __func__, + (u32)jiffies_to_msecs(self->last_tx_data_timestamp), + self->tx_data_cnt, self->tx_xfer_cnt, self->tx_proc_cnt); + mutex_lock(&self->sbus_mutex); + sdio_claim_host(self->func); + bes2600_sdio_reg_read(self, BES_TX_CTRL_REG_ID + 1, &cfg, 1); + bes2600_err(BES2600_DBG_SDIO, "realtime ctrl=%x\n", cfg); + cfg = BES_HOST_INT | BES_SUBSYSTEM_WIFI_DEBUG; + sdio_writeb(self->func, 0, BES_HOST_INT_REG_ID + 1, &ret); + sdio_writeb(self->func, cfg, BES_HOST_INT_REG_ID, &ret); + sdio_release_host(self->func); + mutex_unlock(&self->sbus_mutex); +} + +#ifndef BES_SDIO_OPTIMIZED_LEN +static u8 const crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static u8 bes_crc8(const u8 *data, unsigned len) +{ + u8 crc = 0; + + while(len--) + crc = crc8_table[crc ^ *data++]; + + return crc; +} +#endif + +int bes2600_sdio_read_ctrl(struct sbus_priv *self, u32 *ctrl_reg) +{ + u8 data[4]; + #ifndef BES_SDIO_OPTIMIZED_LEN + u8 check; + u16 pkts, len; + #endif + int ret = 0, again = 0; + *ctrl_reg = 0; + + /* clear sdio slave gen interrupt */ + ret = bes2600_sdio_reg_read(self, BES_TX_CTRL_REG_ID + 1, data, 1); + #ifndef BES_SDIO_OPTIMIZED_LEN + ret = bes2600_sdio_reg_read(self, BES_TX_NEXT_LEN_REG_ID, data, 4); + #endif + if (unlikely(ret)) { + bes2600_err(BES2600_DBG_SDIO, "[SBUS] Failed(%d) to read control register.\n", ret); + return ret; + } + self->rx_total_ctrl_cnt++; + + #ifndef BES_SDIO_OPTIMIZED_LEN + check = bes_crc8((const u8 *)data, 3); + if (data[3] == check) { + /* length field crc8 pass */ + *ctrl_reg = *(u32 *)data; + if (((data[2] >> 7) & 0x1) == self->next_toggle) { + /* toggle valid */ + *ctrl_reg &= (~0xff800000); + } else { + /* last toggle */ + *ctrl_reg = 0; + again = 1; + } + + if (*ctrl_reg) { + /* length field valid */ + again = 1; + pkts = ((*ctrl_reg) >> 16) & 0x7f; + len = (*ctrl_reg) & 0xffff; + if (pkts && len) { + self->next_toggle_debug = *(u32 *)data; + self->next_toggle ^= 1; + } else { + *ctrl_reg = 0; + } + } + } else { + /* length field crc fail */ + //pr_err("%s, crc err:%x,%x,%x,%x,%x\n", __func__, data[0], data[1], data[2], data[3], check); + //msleep(1); + *ctrl_reg = 0; + again = 1; + } + #else + if (data[0] & 0x7f) { + /* length field valid */ + again = 1; + if (((data[0] >> 7) & 0x1) == self->next_toggle) { + *ctrl_reg = (data[0] & 0x7f) << 9; + self->rx_valid_ctrl = data[0]; + if (self->rx_last_ctrl && (((self->rx_last_ctrl >> 7) & 0x1) != self->next_toggle)) + self->rx_continuous_ctrl_cnt++; + self->next_toggle ^= 1; + } else { + self->rx_remain_ctrl_cnt++; + } + } else { + /* distinguish zero true or false */ + self->rx_zero_ctrl_cnt++; + ret = bes2600_sdio_reg_read(self, BES_TX_CTRL_REG_ID, &data[1], 1); + if (!ret && (data[1] & 0x01)) + again = 0; + else + again = 1; + } + self->rx_last_ctrl = data[0]; + #endif + return again; +} + +#ifdef BES_SDIO_RX_MULTIPLE_ENABLE + +static int bes2600_sdio_packets_check(u32 ctrl_reg, u8 *packets) +{ + int i; + u16 single, total_cal = 0; + u16 packets_length; + struct HI_MSG_HDR *pMsg; + + /* bit 23-16 indicate count of packets */ + u32 packets_cnt; + #ifndef BES_SDIO_OPTIMIZED_LEN + packets_cnt = PACKET_COUNT(ctrl_reg); + if (WARN_ON(packets_cnt > BES_SDIO_RX_MULTIPLE_NUM)) + return -200; + #else + packets_cnt = BES_SDIO_RX_MULTIPLE_NUM; + #endif + + /* bit 15-0 indicate totoal length of packets */ + packets_length = PACKET_TOTAL_LEN(ctrl_reg); + + /* first 32-bit: addr in mcu; + * second 32-bit: packet length; + * next: data + */ + for (i = 0; i < packets_cnt; i++) { + pMsg = (struct HI_MSG_HDR *)(packets + total_cal); + bes2600_dbg(BES2600_DBG_SDIO, "%s, %x,%x\n", __func__, pMsg->MsgId, pMsg->MsgLen); + single = pMsg->MsgLen; + single = (single + 3) & (~0x3); + if (unlikely(single > 1632)) { + bes2600_warn(BES2600_DBG_SDIO, "%s %d,len=%u,%dth,total=%u,%u\n", __func__, __LINE__, single, i, + packets_length, total_cal); + if (i >= 1) { + return -201; + } + } + total_cal += single; + #ifdef BES_SDIO_OPTIMIZED_LEN + if ((!pMsg->MsgLen) || (total_cal == packets_length)) { + //pr_info("%s, contain %d packets\n", __func__, i); + break; + } + #endif + } + bes2600_dbg(BES2600_DBG_SDIO, "%s, %d,%u,%u\n", __func__, packets_cnt, packets_length, total_cal); + + #ifndef BES_SDIO_OPTIMIZED_LEN + if (WARN_ON(packets_length != total_cal)) { + return -202; + } + #else + if (packets_length < total_cal) { + pr_err("%s,%d pkt len=%u, total len=%u", __func__, __LINE__, packets_length, total_cal); + return -202; + } + #endif + + return 0; +} + +static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 *data) +{ + int i, alloc_retry = 0; + #ifndef BES_SDIO_OPTIMIZED_LEN + u8 packets_cnt = PACKET_COUNT(ctrl_reg); + #else + u8 packets_cnt = BES_SDIO_RX_MULTIPLE_NUM; + #endif + u16 packet_len, pos = 0; + struct sk_buff *skb; + + for (i = 0; i < packets_cnt; i++) { + packet_len = ((struct HI_MSG_HDR *)&(data[pos]))->MsgLen; + #ifdef BES_SDIO_OPTIMIZED_LEN + if (!packet_len) + break; + #endif + do { + skb = dev_alloc_skb(packet_len); + if (likely(skb)) + break; + bes2600_warn(BES2600_DBG_SDIO, "%s,%d no memory and sleep\n", __func__, __LINE__); + msleep(100); + ++alloc_retry; + } while(alloc_retry < 10); + if (WARN_ON(!skb)) { + return -ENOMEM; + } + skb_trim(skb, 0); + skb_put(skb, packet_len); + memcpy(skb->data, &data[pos], packet_len); + bes2600_dbg(BES2600_DBG_SDIO, "%s, %d,%d\n", __func__, packet_len, pos); + spin_lock(&self->rx_queue_lock); + skb_queue_tail(&self->rx_queue, skb); + self->rx_data_cnt++; + spin_unlock(&self->rx_queue_lock); + packet_len = (packet_len + 3) & (~0x3); + pos += packet_len; + #ifdef BES_SDIO_OPTIMIZED_LEN + if (pos == PACKET_TOTAL_LEN(ctrl_reg)) + break; + #endif + } + return 0; +} + +static void sdio_rx_work(struct work_struct *work) +{ + int ret, again = 0, retry = 0, crc_retry = 0; + u32 ctrl_reg = 0; + int total_len; + struct sbus_priv *self = container_of(work, struct sbus_priv, rx_work); + u8 *buf = self->rx_buffer; + + /* don't read/write sdio when sdio error */ + if(bes2600_chrdev_is_bus_error()) + return; + + bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX); + + do { + bes2600_sdio_lock(self); + again = bes2600_sdio_read_ctrl(self, &ctrl_reg); + + if(again == -EBUSY || again == -ETIMEDOUT) { + bes2600_err(BES2600_DBG_SDIO, "%s sdio read error\n", __func__); + bes2600_sdio_unlock(self); + goto failed; + } + + total_len = PACKET_TOTAL_LEN(ctrl_reg); + if (!total_len) { + bes2600_sdio_unlock(self); + if ((again == 1) && retry <= 5) { + retry++; + continue; + } else { + break; + } + } + + do { + ret = bes2600_sdio_memcpy_fromio(self, 0, buf, total_len); + if (likely(ret != -84)) { + crc_retry = 0; + break; + } else { + crc_retry++; + bes2600_err(BES2600_DBG_SDIO, "%s sdio read crc error(%d)\n", __func__, crc_retry); + } + } while (crc_retry <= 10); + if (self->retune_protected == true) { + sdio_retune_release(self->func); + self->retune_protected = false; + } + bes2600_sdio_unlock(self); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "%s,%d error=%d\n", __func__, __LINE__, ret); + sdio_work_debug(self); + goto failed; + } + retry = 0; + self->rx_xfer_cnt++; + self->last_rx_data_timestamp = jiffies; + + if ((ret = bes2600_sdio_packets_check(ctrl_reg, buf))) { + bes2600_err(BES2600_DBG_SDIO, "%s,%d error=%d\n", __func__, __LINE__, ret); + sdio_work_debug(self); + goto failed; + } + + if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) { + bes2600_err(BES2600_DBG_SDIO, "%s,%d error=%d\n", __func__, __LINE__, ret); + goto failed; + } + + ctrl_reg = 0; + + if (likely(self->irq_handler)) { + self->irq_handler(self->irq_priv); + } else { + bes2600_err(BES2600_DBG_SDIO, "%s,%d\n", __func__, __LINE__); + goto failed; + } + + } while (again); + + bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); + return; + +failed: + bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); + bes2600_chrdev_wifi_force_close(self->core, false); + WARN_ON(1); +} + +static void sdio_scan_work(struct work_struct *work) +{ +#ifdef PLAT_ALLWINNER + //sw_mci_rescan_card(AW_SDIOID, 1); + sunxi_mmc_rescan_card(sunxi_wlan_get_bus_index()); +#endif + +#ifdef PLAT_ROCKCHIP + rockchip_wifi_set_carddetect(1); +#endif + bes2600_info(BES2600_DBG_SDIO, "%s: power down, rescan card\n", __FUNCTION__); +} + +static void *bes2600_sdio_pipe_read(struct sbus_priv *self) +{ + struct sk_buff *skb; + + if(bes2600_chrdev_is_bus_error()) { + return bes2600_tx_loop_read(self->core); + } + + spin_lock(&self->rx_queue_lock); + skb = skb_dequeue(&self->rx_queue); + if (skb) + self->rx_proc_cnt++; + spin_unlock(&self->rx_queue_lock); + if (likely(self->fw_started == true && + !bes2600_pwr_device_is_idle(self->core) && + self->core->hw_bufs_used > 0)) + if (!skb) + queue_work(self->sdio_wq, &self->rx_work); + return skb; +} + +#endif + +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + +struct bes_sdio_tx_list_t { + struct list_head node; + u8 *buf; + u32 len; +}; + +static int bes_sdio_memcpy_to_io_helper(struct sdio_func *func, unsigned origin_size, struct scatterlist *sg, u32 sg_num) +{ + int ret = 0; + u32 align_blocks; + u32 compensate; + unsigned size = origin_size & (~0x3); + +#ifdef BES_SDIO_RXTX_TOGGLE + struct sbus_priv *self = NULL; +#endif + +#ifdef SDIO_HOST_ADMA_SUPPORT + u32 sg_compensate_num = sg_num; +#else + int i, already, separate; + struct scatterlist sg_copy, *element; +#endif + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + +#ifdef BES_SDIO_RXTX_TOGGLE + self = sdio_get_drvdata(func); +#endif + + if (size && func->card->cccr.multi_block) { + + align_blocks = (size + func->cur_blksize - 1) / func->cur_blksize; + bes2600_dbg(BES2600_DBG_SDIO, "%s sz=%u blk=%u", __func__, size, align_blocks); + compensate = align_blocks * func->cur_blksize - size; +#ifdef SDIO_HOST_ADMA_SUPPORT + if (compensate) { + sg_set_buf(&sg[sg_num], self->tx_buffer, compensate); + sg_compensate_num = sg_num + 1; + } + sg_mark_end(&sg[sg_compensate_num - 1]); +#else + if (WARN_ON(size + compensate > MAX_SDIO_TRANSFER_LEN)) + return -EINVAL; + already = 0; + separate = origin_size & 3; + if (separate == 3) + separate = 1632; + else + separate = (separate + 1) * 512; + for (i = 0; i < sg_num; i++) { + element = &sg[i]; + memcpy(&self->single_gathered_buffer[already], page_address(sg_page(element)) + element->offset, + element->length); + already += separate; + } + sg_set_buf(&sg_copy, self->single_gathered_buffer, size + compensate); +#endif + + memset(&mrq, 0, sizeof(mrq)); + memset(&cmd, 0, sizeof(cmd)); + memset(&data, 0, sizeof(data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_IO_RW_EXTENDED; + cmd.arg = 0x80000000; + cmd.arg |= func->num << 28; + cmd.arg |= 0x04000000; + cmd.arg |= (origin_size) << 9; + +#ifdef BES_SDIO_RXTX_TOGGLE + if (likely(self->fw_started == true)) { + cmd.arg &= ~(1 << 25); + cmd.arg |= ((self->tx_data_toggle & 0x1) << 25); + ++self->tx_data_toggle; + } +#endif + + cmd.arg |= 0x08000000 | align_blocks; + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + data.blksz = func->cur_blksize; + data.blocks = align_blocks; + data.flags = MMC_DATA_WRITE; + +#ifdef SDIO_HOST_ADMA_SUPPORT + data.sg = sg; + data.sg_len = sg_compensate_num; +#else + data.sg = &sg_copy; + data.sg_len = 1; +#endif + + mmc_set_data_timeout(&data, func->card); + + mmc_wait_for_req(func->card->host, &mrq); + + if (cmd.error){ + ret = cmd.error; + goto out; + } + if (data.error) { + ret = data.error; + goto out; + } + if (cmd.resp[0] & R5_ERROR) { + ret = -EIO; + goto out; + } + if (cmd.resp[0] & R5_FUNCTION_NUMBER) { + ret = -EINVAL; + goto out; + } + if (cmd.resp[0] & R5_OUT_OF_RANGE) { + ret = -ERANGE; + goto out; + } + } else { + bes2600_err(BES2600_DBG_SDIO, "%s,%d (%u)\n", __func__, __LINE__, size); + ret = -EINVAL; + } +out: +#ifdef BES_SDIO_RXTX_TOGGLE + if (unlikely(ret)) + self->tx_data_toggle--; +#endif + return ret; +} + +static void sdio_tx_work(struct work_struct *work) +{ + int ret, crc_retry = 0; + u32 blks, cur_blk = 0, align, total_len = 0, scatters = 0; + struct list_head proc_list; + struct bes_sdio_tx_list_t *tx_buffer, *temp; + struct sbus_priv *self = container_of(work, struct sbus_priv, tx_work); + struct scatterlist *sg = NULL; + int bes_sdio_tx_multiple_num; + struct HI_MSG_HDR *pMsg; + enum DRIVER_TO_MCU_MSG_ST driver_to_mcu = ST_EXIT; + + /* don't read/write sdio when sdio error */ + if(bes2600_chrdev_is_bus_error()) + return; + + if (bes2600_chrdev_is_signal_mode()) { + sg = self->tx_sg; + bes_sdio_tx_multiple_num = BES_SDIO_TX_MULTIPLE_NUM; + } else { + sg = self->tx_sg_nosignal; + bes_sdio_tx_multiple_num = BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL; + } + + INIT_LIST_HEAD(&proc_list); + + for (;;) { + spin_lock(&self->tx_bufferlock); + list_splice_tail_init(&self->tx_bufferlist, &proc_list); + spin_unlock(&self->tx_bufferlock); + if (list_empty(&proc_list)) + break; + sg_init_table(sg, bes_sdio_tx_multiple_num + 1); + list_for_each_entry_safe(tx_buffer, temp, &proc_list, node) { + blks = (tx_buffer->len + self->func->cur_blksize - 1) / self->func->cur_blksize; + align = blks * self->func->cur_blksize; + if (blks >= 4) { + align = 1632; + } + if (unlikely(blks >= 5)) { + bes2600_err(BES2600_DBG_SDIO, "%s,%d skip error-len packet:%u,%d\n", __func__, __LINE__, tx_buffer->len, blks); + list_del_init(&tx_buffer->node); + kmem_cache_free(self->tx_bufferlistpool, tx_buffer); + continue; + } + bes2600_dbg(BES2600_DBG_SDIO, "%s,%p,%u->%u\n", __func__, tx_buffer->buf, tx_buffer->len, align); + if (!cur_blk) + cur_blk = blks; + else if (cur_blk != blks) + goto flush_previous; + + pMsg = (struct HI_MSG_HDR *)tx_buffer->buf; + if (unlikely(IS_DRIVER_VENDOR_CMD(pMsg->MsgId))) { + if (driver_to_mcu == ST_EXIT) { + driver_to_mcu = ST_ENTER; + goto flush_previous; + } + } + + sg_set_buf(&sg[scatters], tx_buffer->buf, align); + total_len += align; + ++scatters; +/*del_node:*/ + list_del_init(&tx_buffer->node); + kmem_cache_free(self->tx_bufferlistpool, tx_buffer); + self->tx_proc_cnt++; + if (unlikely(IS_DRIVER_VENDOR_CMD(pMsg->MsgId))) { + if (driver_to_mcu == ST_ENTER) { + driver_to_mcu = ST_EXIT; + break; + } + } + if (scatters >= bes_sdio_tx_multiple_num) { + break; + } + } +flush_previous: + if (likely(scatters)) { + if (WARN_ON(total_len & 0x3)) + break; + else + total_len |= (cur_blk - 1); + sdio_claim_host(self->func); + if (self->retune_protected == false) { + sdio_retune_hold_now(self->func); + self->retune_protected = true; + } + do { + ret = bes_sdio_memcpy_to_io_helper(self->func, total_len, sg, scatters); + if (likely(ret != -84)) { + crc_retry = 0; + break; + } else { + crc_retry++; + bes2600_err(BES2600_DBG_SDIO, "%s sdio write crc error(%d)\n", __func__, crc_retry); + } + } while (crc_retry <= 10); + sdio_release_host(self->func); + queue_work(self->sdio_wq, &self->rx_work); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk); + sdio_work_debug(self); + bes2600_chrdev_wifi_force_close(self->core, false); + } + scatters = 0; + total_len = 0; + cur_blk = 0; + self->tx_xfer_cnt++; + self->last_tx_data_timestamp = jiffies; + } + } +} + +static int bes2600_sdio_pipe_send(struct sbus_priv *self, u8 pipe, u32 len, u8 *buf) +{ + struct bes_sdio_tx_list_t * desc = NULL; + + if(bes2600_chrdev_is_bus_error()) { + bes2600_tx_loop_pipe_send(self->core, buf, len); + return 0; + } + + desc = kmem_cache_alloc(self->tx_bufferlistpool, GFP_KERNEL); + if (!desc) + return -ENOMEM; + INIT_LIST_HEAD(&desc->node); + desc->buf = buf; + desc->len = len; + if (!buf || !len) + return -EINVAL; + + spin_lock(&self->tx_bufferlock); + list_add_tail(&desc->node, &self->tx_bufferlist); + self->tx_data_cnt++; + spin_unlock(&self->tx_bufferlock); + queue_work(self->sdio_wq, &self->tx_work); + return 0; + +} +#endif + +static int bes2600_sdio_misc_init(struct sbus_priv *self, struct bes2600_common *ar) +{ +#ifdef BES_SDIO_RXTX_TOGGLE + self->rx_data_toggle = 0; + self->tx_data_toggle = 0; + self->next_toggle = 0; +#endif +#ifdef BES_SDIO_RX_MULTIPLE_ENABLE + spin_lock_init(&self->rx_queue_lock); + skb_queue_head_init(&self->rx_queue); + self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM)); + if (!self->rx_buffer) + return -ENOMEM; + INIT_WORK(&self->rx_work, sdio_rx_work); +#endif +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + INIT_LIST_HEAD(&self->tx_bufferlist); + spin_lock_init(&self->tx_bufferlock); + self->tx_buffer = (u8 *)kmalloc(512, GFP_KERNEL); + if (!self->tx_buffer) { + goto err2; + } + self->tx_bufferlistpool = kmem_cache_create("sdio_tx_bufferlistpool", sizeof(struct bes_sdio_tx_list_t), 0, SLAB_HWCACHE_ALIGN, NULL); + if (!self->tx_bufferlistpool) + goto err1; + self->sdio_wq = alloc_workqueue("bes_sdio", WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_CPU_INTENSIVE, 2); + if (!self->sdio_wq) + goto err0; + INIT_WORK(&self->tx_work, sdio_tx_work); + return 0; +err0: + kmem_cache_destroy(self->tx_bufferlistpool); +err1: + kfree(self->tx_buffer); +err2: + free_pages((unsigned long)self->rx_buffer, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM)); + return -ENOMEM; +#endif + return 0; +} + +#if defined(PLAT_ALLWINNER)|| defined (PLAT_ROCKCHIP) +static struct bes2600_platform_data_sdio bes_sdio_plat_data = { +#if defined(BES2600_INDEPENDENT_EVB) + .reset = GPIOA(9), + .powerup = GPIOC(3), + .wakeup = -1, +#elif defined(BES2600_INTEGRATED_MODULE_V1) + .reset = GPIOA(0), + .powerup = -1, + .wakeup = -1, +#elif defined(BES2600_INTEGRATED_MODULE_V2) + .reset = GPIOM(2), + .powerup = -1, + .wakeup = GPIOM(5), +#elif defined(PLAT_ROCKCHIP) + .reset = -1, + .powerup = -1, + .wakeup = -1, +#endif +}; +#endif + +struct bes2600_platform_data_sdio *bes2600_get_platform_data(void) +{ +#if defined(PLAT_ALLWINNER)|| defined (PLAT_ROCKCHIP) + return &bes_sdio_plat_data; +#else + return NULL; +#endif +} + +static void bes2600_get_gpio_from_dts(int *gpio_num, const char *gpio_name) +{ + int wakeup_gpio; + enum of_gpio_flags flags; + struct device_node *wireless_node; + wireless_node = of_find_node_with_property(NULL, gpio_name); + if(wireless_node != NULL){ + wakeup_gpio = of_get_named_gpio_flags(wireless_node, gpio_name, 0, &flags); + if (gpio_is_valid(wakeup_gpio)) + *gpio_num = wakeup_gpio; + }else{ + bes2600_err(BES2600_DBG_SDIO, "find node for %s failed\n", gpio_name); + } +} + +static int bes2600_platform_data_init(void) +{ + int ret = 0; + struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + if (pdata == NULL) + return 0; + + /* Ensure I/Os are pulled low */ + if (gpio_is_valid(pdata->reset)) { + ret = gpio_request(pdata->reset, "bes2600_wlan_reset"); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "can't reqest reset_gpio:%d\n", ret); + goto exit; + } else { + gpio_direction_output(pdata->reset, 0); + } + } else { + bes2600_err(BES2600_DBG_SDIO, "reset is invalid\n"); + } + + if (gpio_is_valid(pdata->powerup)) { + ret = gpio_request(pdata->powerup, "bes2600_wlan_powerup"); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "can't request powerup_gpio:%d\n", ret); + goto exit; + } else { + gpio_direction_output(pdata->powerup, 0); + } + } else { + bes2600_err(BES2600_DBG_SDIO, "powerup is invalid\n"); + } + + bes2600_get_gpio_from_dts(&pdata->wakeup, "WIFI,host_wakeup_wifi"); + if (gpio_is_valid(pdata->wakeup)) { + ret = gpio_request(pdata->wakeup, "bes2600_wakeup"); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "can't request wakeup_gpio:%d\n", ret); + goto exit; + } else { + gpio_direction_output(pdata->wakeup, 0); + } + } else { + bes2600_err(BES2600_DBG_SDIO, "wakeup is invalid\n"); + } + + bes2600_get_gpio_from_dts(&pdata->host_wakeup, "WIFI,host_wake_irq"); + if (gpio_is_valid(pdata->host_wakeup)) { + ret = gpio_request(pdata->host_wakeup, "bes2600_host_irq"); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "can't reqest host_wake_gpio:%d\n", ret); + goto exit; + } else { + gpio_direction_input(pdata->host_wakeup); + } + } else { + bes2600_err(BES2600_DBG_SDIO, "host_wakeup is invalid\n"); + } + + pdata->wlan_bt_hostwake_registered = false; +exit: + return ret; +} + +static void bes2600_platform_data_deinit(void) +{ + const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + if (pdata == NULL) { + return; + } + + if (gpio_is_valid(pdata->reset)) { + gpio_free(pdata->reset); + } + if (gpio_is_valid(pdata->powerup)) { + gpio_free(pdata->powerup); + } + if (gpio_is_valid(pdata->wakeup)) { + gpio_free(pdata->wakeup); + } + if (gpio_is_valid(pdata->host_wakeup)) { + gpio_free(pdata->host_wakeup); + } +} + +static int bes2600_sdio_reset(struct sbus_priv *self) +{ + const struct bes2600_platform_data_sdio *plat_data = bes2600_get_platform_data(); + + bes2600_info(BES2600_DBG_SDIO, "%s ...\n", __func__); + + if (plat_data == NULL) + return 0; + + if (plat_data->reset) { + gpio_set_value(plat_data->reset, 1); + mdelay(50); + gpio_set_value(plat_data->reset, 0); + } + return 0; +} + +static int bes2600_sdio_readb_safe(struct sdio_func *func, unsigned int addr) +{ + int ret = 0; + u8 val = 0; + u8 retry = 0; + + do { + val = sdio_readb(func, addr, &ret); + } while((ret < 0) && ++retry < 30); + + bes2600_err_with_cond(ret, BES2600_DBG_SDIO, "%s failed, ret:%d\n", __func__, ret); + + return (ret < 0) ? ret : val; +} + +static int bes2600_sdio_writeb_safe(struct sdio_func *func, unsigned int addr, u8 val) +{ + int ret; + u8 retry = 0; + + do { + sdio_writeb(func, val, addr, &ret); + } while((ret < 0) && ++retry < 30); + + bes2600_err_with_cond(ret, BES2600_DBG_SDIO, "%s failed, ret:%d\n", __func__, ret); + + return ret; +} + +static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag) +{ + bool gpio_wakeup = false; + const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + if (pdata == NULL) + return; + + bes2600_dbg(BES2600_DBG_SDIO, "%s with %d\n", __func__, flag); + + mutex_lock(&self->io_mutex); + + /* error check */ + if((self->gpio_wakup_flags & BIT(flag)) != 0) { + bes2600_err(BES2600_DBG_SDIO, + "repeat set gpio_wake_flag, sub_sys:%d", flag); + mutex_unlock(&self->io_mutex); + return; + } + + /* check if this is the first subsystem that need mcu to keep awake */ + gpio_wakeup = (self->gpio_wakup_flags == 0); + + /* do wakeup mcu operation */ + if(gpio_wakeup) { + bes2600_dbg(BES2600_DBG_SDIO, "pull high gpio by flag:%d\n", flag); + if (gpio_is_valid(pdata->wakeup)) { + gpio_set_value(pdata->wakeup, 1); + msleep(10); + } else { + bes2600_err(BES2600_DBG_SDIO, + "%s, wakeup gpio is invalid\n", __func__); + } + } + + /* set flag of gpio_wakeup_flags */ + self->gpio_wakup_flags |= BIT(flag); + + mutex_unlock(&self->io_mutex); +} + +static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag) +{ + bool gpio_sleep = false; + const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + if (pdata == NULL) + return; + + bes2600_dbg(BES2600_DBG_SDIO, "%s with %d\n", __func__, flag); + + mutex_lock(&self->io_mutex); + + /* error check */ + if((self->gpio_wakup_flags & BIT(flag)) == 0) { + bes2600_err(BES2600_DBG_SDIO, + "repeat clear gpio_wake_flag, sub_sys:%d", flag); + mutex_unlock(&self->io_mutex); + return; + } + + /* clear flag of gpio_wakeup_flags */ + self->gpio_wakup_flags &= ~BIT(flag); + + /* check if this is the last subsystem that need mcu to keep awake */ + gpio_sleep = (self->gpio_wakup_flags == 0); + + /* do wakeup mcu operation */ + if(gpio_sleep) { + bes2600_dbg(BES2600_DBG_SDIO, "pull low gpio by flag:%d\n", flag); + if (gpio_is_valid(pdata->wakeup)) { + gpio_set_value(pdata->wakeup, 0); + } else { + bes2600_err(BES2600_DBG_SDIO, + "%s, wakeup gpio is invalid\n", __func__); + } + } + + mutex_unlock(&self->io_mutex); +} + +int bes2600_sdio_active(struct sbus_priv *self, int sub_system) +{ + u16 cfg; + u8 cfm = 0; + int ret = 0, retries = 0; + u8 tmp_val = 0; + u32 cnt = 0; + u32 delay_cnt = 2; + + /* nosignal mode only allow SUBSYSTEM_WIFI */ + if (!bes2600_chrdev_is_signal_mode() && sub_system != SUBSYSTEM_WIFI) + return -EINVAL; + + /* don't read/write sdio when sdio error */ + if(bes2600_chrdev_is_bus_error()) + return 0; + + /* prevent concurrent access */ + mutex_lock(&self->sbus_mutex); + + /* set config and confirm value */ + if (sub_system == SUBSYSTEM_MCU) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_MCU_ACTIVE; + cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY; + delay_cnt = 2; + } else if (sub_system == SUBSYSTEM_WIFI) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_WIFI_ACTIVE; + cfm = BES_SLAVE_STATUS_WIFI_READY; + delay_cnt = 25; + } else if(sub_system == SUBSYSTEM_BT) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_ACTIVE; + cfm = BES_SLAVE_STATUS_BT_READY; + delay_cnt = 25; + } else if(sub_system == SUBSYSTEM_BT_LP) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_WAKEUP; + cfm = BES_SLAVE_STATUS_BT_WAKE_READY; + delay_cnt = 2; + } else { + mutex_unlock(&self->sbus_mutex); + return -EINVAL; + } + + /* set fw_started flag in advance */ + if(sub_system == SUBSYSTEM_WIFI) { + self->fw_started = true; + } + + /* wait until device ready */ + do { + sdio_claim_host(self->func); + ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID); + sdio_release_host(self->func); + bes2600_dbg(BES2600_DBG_SDIO, "active wait mcu ready cnt:%d, reg:%d\n", cnt++, ret); + if(ret < 0) { + goto err; + } + } while((ret & BES_SLAVE_STATUS_MCU_READY) == 0); + + do { + /* claim sdio host */ + sdio_claim_host(self->func); + + /* write first segment */ + tmp_val = (cfg >> 8) & 0xff; + ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID + 1, tmp_val); + if(ret < 0) { + sdio_release_host(self->func); + bes2600_err(BES2600_DBG_SDIO, "active write 1st seg failed\n"); + goto err; + } + + /* write second segment */ + tmp_val = cfg & 0xff; + ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID, tmp_val); + if(ret < 0) { + sdio_release_host(self->func); + bes2600_err(BES2600_DBG_SDIO, "active write 2nd seg failed\n"); + goto err; + } + + /* release sdio host */ + sdio_release_host(self->func); + + /* wait device to response */ + msleep(delay_cnt); + + /* read device response result */ + sdio_claim_host(self->func); + ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID); + sdio_release_host(self->func); + if(ret < 0) { + bes2600_err(BES2600_DBG_SDIO, "active read response failed\n"); + goto err; + } + bes2600_dbg(BES2600_DBG_SDIO, "active resp cnt:%d, reg:%d, sub_sys:%d\n", retries, ret, sub_system); + } while ((cfm != 0) && (ret & cfm) == 0 && ++retries <= 200); // check if cfm bit is set + + if (retries > 200) { + bes2600_err(BES2600_DBG_SDIO, "bes2600_sdio_active failed, subsys:%d\n", sub_system); + /* open wifi failed, restore fw_started flag */ + if(sub_system == SUBSYSTEM_WIFI) { + self->fw_started = false; + } + + mutex_unlock(&self->sbus_mutex); + return -EFAULT; + } else { + ret = 0; + } + +#ifdef BES2600_GPIO_WAKEUP_AP + if (sub_system == SUBSYSTEM_WIFI || + sub_system == SUBSYSTEM_BT) + ret = bes2600_gpio_wakeup_ap_config(self); +#else + ret = 0; +#endif + /* prevent concurrent access */ + mutex_unlock(&self->sbus_mutex); + + return ret; +err: + mutex_unlock(&self->sbus_mutex); + bes2600_chrdev_wifi_force_close(self->core, false); + return -ENODEV; +} + +static void bes2600_sdio_empty_work(struct sbus_priv *self) +{ +#ifdef BES_SDIO_RX_MULTIPLE_ENABLE + struct sk_buff *skb; +#endif +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + struct bes_sdio_tx_list_t *tx_buffer, *temp; +#endif + +#ifdef BES_SDIO_RX_MULTIPLE_ENABLE + cancel_work_sync(&self->rx_work); + while (1) { + skb = skb_dequeue(&self->rx_queue); + if (skb) + dev_kfree_skb(skb); + else + break; + } + self->rx_last_ctrl = 0; + self->rx_total_ctrl_cnt = 0; + self->rx_continuous_ctrl_cnt = 0; + self->rx_remain_ctrl_cnt = 0; + self->rx_zero_ctrl_cnt = 0; + self->rx_data_cnt = 0; + self->rx_xfer_cnt = 0; + self->rx_proc_cnt = 0; +#endif + +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + cancel_work_sync(&self->tx_work); + list_for_each_entry_safe(tx_buffer, temp, &self->tx_bufferlist, node) { + list_del_init(&tx_buffer->node); + kmem_cache_free(self->tx_bufferlistpool, tx_buffer); + } + self->tx_data_cnt = 0; + self->tx_xfer_cnt = 0; + self->tx_proc_cnt = 0; +#endif + +#ifdef BES_SDIO_RXTX_TOGGLE + self->rx_data_toggle = 0; + self->tx_data_toggle = 0; + self->next_toggle = 0; +#endif +} + +#ifdef BES2600_GPIO_WAKEUP_AP +static void bes2600_wlan_bt_hostwake_unregister(void); +#endif + +int bes2600_sdio_deactive(struct sbus_priv *self, int sub_system) +{ + u16 cfg = 0; + u8 cfm = 0; + u8 tmp_val = 0; + u16 retries = 0; + u32 cnt = 0; + u32 delay_cnt = 2; + int ret; + + /* don't read/write sdio when sdio error */ + if(bes2600_chrdev_is_bus_error()) + return 0; + + /* notify device deactive event */ + if (bes2600_chrdev_is_signal_mode()) { + /* prevent concurrent access */ + mutex_lock(&self->sbus_mutex); + + /* set config and confirm value */ + if (sub_system == SUBSYSTEM_MCU) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_MCU_DEACTIVE; + cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY; + } else if (sub_system == SUBSYSTEM_WIFI) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_WIFI_DEACTIVE; + cfm = BES_SLAVE_STATUS_WIFI_READY; + } else if(sub_system == SUBSYSTEM_BT) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_DEACTIVE; + cfm = BES_SLAVE_STATUS_BT_READY; + } else if(sub_system == SUBSYSTEM_BT_LP) { + cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_SLEEP; + cfm = BES_SLAVE_STATUS_BT_WAKE_READY; + } else { + mutex_unlock(&self->sbus_mutex); + return -EINVAL; + } + + /* wait until device ready */ + do { + sdio_claim_host(self->func); + ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID); + sdio_release_host(self->func); + bes2600_dbg(BES2600_DBG_SDIO, "deactive wait mcu ready cnt:%d, reg:%d\n", cnt++, ret); + + if(ret < 0) { + goto err; + } + } while((ret & BES_SLAVE_STATUS_MCU_READY) == 0); + + do { + /* claim sdio host */ + sdio_claim_host(self->func); + + /* write first segment */ + tmp_val = (cfg >> 8) & 0xff; + ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID + 1, tmp_val); + if(ret < 0) { + sdio_release_host(self->func); + bes2600_err(BES2600_DBG_SDIO, "deactive write 1st seg failed\n"); + goto err; + } + + /* write second segment */ + tmp_val = cfg & 0xff; + ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID, tmp_val); + if(ret < 0) { + sdio_release_host(self->func); + bes2600_err(BES2600_DBG_SDIO, "deactive write 2nd seg failed\n"); + goto err; + } + + /* release sdio host */ + sdio_release_host(self->func); + + /* wait device to response */ + msleep(delay_cnt); + + /* read device response result */ + sdio_claim_host(self->func); + ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID); + sdio_release_host(self->func); + if(ret < 0) { + bes2600_err(BES2600_DBG_SDIO, "deactive read response failed\n"); + if (sub_system == SUBSYSTEM_MCU) { + /* cmd52 may return error when 2600 is sleeping */ + ret = 0; + break; + } else { + goto err; + } + } + bes2600_dbg(BES2600_DBG_SDIO, "deactive resp cnt:%d, reg:%d, sub_sys:%d\n", retries, ret, sub_system); + } while((cfm != 0) && (ret & cfm) != 0 && ++retries < 200); + + /* set fw_started flag to false */ + if(bes2600_chrdev_is_signal_mode() + && sub_system == SUBSYSTEM_WIFI) + self->fw_started = false; + + /* reset sdio send and receive control variable */ + if(sub_system == SUBSYSTEM_WIFI) { + bes2600_sdio_empty_work(self); + } + + /* prevent concurrent access */ + mutex_unlock(&self->sbus_mutex); + + return (ret < 0) ? ret : 0; + } else { + return 0; + } + +err: + mutex_unlock(&self->sbus_mutex); + bes2600_chrdev_wifi_force_close(self->core, false); + return -ENODEV; +} + +static int bes2600_sdio_power_up(void) +{ + const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + int ret; + + ret = bes2600_sdio_on(pdata); + if (ret) + goto err_on; + + +#ifdef PLAT_ALLWINNER + mdelay(10); + bes2600_chrdev_start_bus_probe(); + //sw_mci_rescan_card(AW_SDIOID, 1); + sunxi_mmc_rescan_card(sunxi_wlan_get_bus_index()); + bes2600_info(BES2600_DBG_SDIO, "%s: power up, rescan card.\n", __FUNCTION__); +#endif + + return 0; + +err_on: + return ret; +} + +static void bes2600_sdio_power_down(struct sbus_priv *self) +{ +#ifdef POWER_DOWN_BY_MSG + u32 cfg = BES_HOST_INT | BES_SUBSYSTEM_SYSTEM_CLOSE; + u8 tmp_val = 0; + int ret = 0; + + sdio_claim_host(self->func); + tmp_val = (cfg >> 8) & 0xff; + sdio_writeb(self->func, tmp_val, BES_HOST_INT_REG_ID + 1, &ret); + tmp_val = cfg & 0xff; + sdio_writeb(self->func, tmp_val, BES_HOST_INT_REG_ID, &ret); + sdio_release_host(self->func); +#else +#if defined(PLAT_ROCKCHIP) + rockchip_wifi_power(0); +#endif + +#if defined(PLAT_ALLWINNER) + sunxi_wlan_set_power(false); +#endif +#endif + + msleep(10); + + self->func->card->host->caps &= ~MMC_CAP_NONREMOVABLE; + schedule_work(&self->sdio_scan_work); + +} + +static int bes2600_sdio_power_switch(struct sbus_priv *self, int on) +{ + int ret = 0; + if(on) { + ret = bes2600_sdio_power_up(); + } else { + bes2600_sdio_power_down(self); + } + + return ret; +} + +static void bes2600_sdio_halt_device(struct sbus_priv *self) +{ + sdio_work_debug(self); +} + +static bool bes2600_sdio_wakeup_source(struct sbus_priv *self) +{ + struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + + if (!pdata) + return false; + + if(pdata->wakeup_source) { + pdata->wakeup_source = false; + return true; + } + + return false; +} + +static struct sbus_ops bes2600_sdio_sbus_ops = { + .sbus_memcpy_fromio = bes2600_sdio_memcpy_fromio, + .sbus_memcpy_toio = bes2600_sdio_memcpy_toio, + .lock = bes2600_sdio_lock, + .unlock = bes2600_sdio_unlock, + .irq_subscribe = bes2600_sdio_irq_subscribe, + .irq_unsubscribe = bes2600_sdio_irq_unsubscribe, + .reset = bes2600_sdio_reset, + .align_size = bes2600_sdio_align_size, + .set_block_size = bes2600_sdio_set_block_size, + .sbus_reg_read = bes2600_sdio_reg_read, + .sbus_reg_write = bes2600_sdio_reg_write, + .init = bes2600_sdio_misc_init, +#ifdef BES_SDIO_RX_MULTIPLE_ENABLE + .pipe_read = bes2600_sdio_pipe_read, +#endif +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + .pipe_send = bes2600_sdio_pipe_send, +#endif + .sbus_active = bes2600_sdio_active, + .sbus_deactive = bes2600_sdio_deactive, + .power_switch = bes2600_sdio_power_switch, + .gpio_wake = bes2600_gpio_wakeup_mcu, + .gpio_sleep = bes2600_gpio_allow_mcu_sleep, + .halt_device = bes2600_sdio_halt_device, + .wakeup_source = bes2600_sdio_wakeup_source, +}; + +static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) +{ + long unsigned int old_ts, new_ts; + struct sbus_priv *self = hw_priv->sbus_priv; + + do { + old_ts = self->last_irq_timestamp; + flush_work(&self->rx_work); + new_ts = self->last_irq_timestamp; + } while(old_ts != new_ts); +} + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int bes2600_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct sbus_priv *self; + int status; + + bes2600_info(BES2600_DBG_SDIO, "Probe called:%p,%d\n", func, func->num); + if (func->num > 1) + return 0; + + func->card->host->caps |= MMC_CAP_NONREMOVABLE; + bes2600_chrdev_bus_probe_notify(); + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + bes2600_dbg(BES2600_DBG_SDIO, "Can't allocate SDIO sbus_priv."); + return -ENOMEM; + } +#ifdef PLAT_ALLWINNER_SUN6I + aw_gpio_irq_handle = 0; +#endif + spin_lock_init(&self->lock); + self->pdata = bes2600_get_platform_data(); + self->func = func; + self->dev = &func->dev; + self->gpio_wakup_flags = 0; + self->retune_protected = false; + self->unregister_in_process = false; + mutex_init(&self->io_mutex); + mutex_init(&self->sbus_mutex); + INIT_WORK(&self->sdio_scan_work, sdio_scan_work); +#ifndef SDIO_HOST_ADMA_SUPPORT + if ((MAX_SDIO_TRANSFER_LEN < 1632 * BES_SDIO_RX_MULTIPLE_NUM) || + (MAX_SDIO_TRANSFER_LEN < 1632 * BES_SDIO_TX_MULTIPLE_NUM)) { + bes2600_err(BES2600_DBG_SDIO, "gathered buffer is too small.\n"); + return -EINVAL; + } + self->single_gathered_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(MAX_SDIO_TRANSFER_LEN)); + if (!self->single_gathered_buffer) + return -ENOMEM; +#endif +#ifdef BES_SDIO_RXTX_TOGGLE + self->fw_started = false; +#endif + bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_PROBE); + + sdio_set_drvdata(func, self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + bes2600_reg_set_object(&bes2600_sdio_sbus_ops, self); + status = bes2600_load_firmware(&bes2600_sdio_sbus_ops, self); + bes2600_info_with_cond((status > 0), BES2600_DBG_SDIO, + "interrupt init process beacuse device be closed.\n"); + if(status > 0) // for wifi closed case + goto out; + else if(status < 0) // for download fail case + goto err; + + status = bes2600_register_net_dev(self); + if (status) { + goto err; + } + +out: + bes2600_chrdev_set_sbus_priv_data(self, false); + bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_PROBE); + return 0; + +err: + bes2600_err(BES2600_DBG_SDIO, "%s failed, func:%d\n", __func__, func->num); + func->card->host->caps &= ~MMC_CAP_NONREMOVABLE; + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_PROBE); + sdio_set_drvdata(func, NULL); + bes2600_reg_set_object(NULL, NULL); + bes2600_chrdev_set_sbus_priv_data(NULL, true); + kfree(self); + return 0; +} + +int bes2600_register_net_dev(struct sbus_priv *bus_priv) +{ + int status = 0; + BUG_ON(!bus_priv); + status = bes2600_core_probe(&bes2600_sdio_sbus_ops, + bus_priv, bus_priv->dev, &bus_priv->core); + if(!status) + bes2600_pwr_register_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb); + + return status; +} + +int bes2600_unregister_net_dev(struct sbus_priv *bus_priv) +{ + BUG_ON(!bus_priv); + if (bus_priv->core && !bus_priv->unregister_in_process) { + bus_priv->unregister_in_process = true; + bes2600_core_release(bus_priv->core); + bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb); + bus_priv->core = NULL; + + if (bus_priv->sdio_wq) { + flush_workqueue(bus_priv->sdio_wq); + destroy_workqueue(bus_priv->sdio_wq); + bus_priv->sdio_wq = NULL; + } + + if (bus_priv->rx_buffer) { + free_pages((unsigned long)bus_priv->rx_buffer, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM)); + bus_priv->rx_buffer = NULL; + } + +#ifdef BES_SDIO_TX_MULTIPLE_ENABLE + if (bus_priv->tx_buffer) { + kfree(bus_priv->tx_buffer); + bus_priv->tx_buffer = NULL; + } + + if (bus_priv->tx_bufferlistpool) { + kmem_cache_destroy(bus_priv->tx_bufferlistpool); + bus_priv->tx_bufferlistpool = NULL; + } +#endif + bus_priv->unregister_in_process = false; + } + return 0; +} + +bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv) +{ + BUG_ON(!bus_priv); + return (bus_priv->core != NULL); +} + +/* Disconnect Function to be called by SDIO stack when + * device is disconnected */ +static void bes2600_sdio_disconnect(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + + func->card->host->caps &= ~MMC_CAP_NONREMOVABLE; + bes2600_info(BES2600_DBG_SDIO, "%s called:%p,%d\n", __func__, func, func->num); + + if (self) { + bes2600_unregister_net_dev(self); +#ifndef CONFIG_BES2600_USE_GPIO_IRQ + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); +#else + free_irq(irq->start, self); +#endif //CONFIG_BES2600_USE_GPIO_IRQ + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + bes2600_sdio_off(pdata); + bes2600_reg_set_object(NULL, NULL); + bes2600_chrdev_set_sbus_priv_data(NULL, false); + sdio_set_drvdata(func, NULL); + if (self->retune_protected == true) { + sdio_retune_release(func); + } +#ifndef SDIO_HOST_ADMA_SUPPORT + if (self->single_gathered_buffer) { + free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN)); + } +#endif + kfree(self); + } +} + +#ifdef BES2600_GPIO_WAKEUP_AP + +#if defined(PLAT_ALLWINNER) +extern int sunxi_wlan_get_oob_irq_flags(void); +extern int sunxi_wlan_get_oob_irq(void); +#elif defined(PLAT_ROCKCHIP) +extern int rockchip_wifi_get_oob_irq_flag(void); +extern int rockchip_wifi_get_oob_irq(void); +#endif + +static irqreturn_t bes2600_wlan_bt_hostwake_thread(int irq, void *dev_id) +{ + struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + + bes2600_info(BES2600_DBG_SDIO, "bes2600_wlan_hostwake:%d\n", dev_id == (void *)pdata); + + if (dev_id == (void *)pdata) { + pdata->wakeup_source = true; + disable_irq_nosync(irq); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int bes2600_wlan_bt_hostwake_register(void) +{ + int ret = 0; + struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + +#if defined(PLAT_ALLWINNER) + int irq_flags = sunxi_wlan_get_oob_irq_flags(); + int irq = sunxi_wlan_get_oob_irq(); +#elif defined(PLAT_ROCKCHIP) + int irq_flags = rockchip_wifi_get_oob_irq_flag(); + int irq = rockchip_wifi_get_oob_irq(); +#endif + + if (pdata->wlan_bt_hostwake_registered == true) { + bes2600_err(BES2600_DBG_SDIO, "wlan hostwake register repeatedly.\n"); + return -1; + } + +#if defined(PLAT_ALLWINNER) || defined(PLAT_ROCKCHIP) + irq_flags |= IRQF_ONESHOT; + irq_flags &= (~IRQF_NO_SUSPEND); + if (((ret = request_threaded_irq(irq, NULL, bes2600_wlan_bt_hostwake_thread, + irq_flags, "bes2600_wlan_hostwake", pdata)) != 0) || + ((ret = enable_irq_wake(irq)) != 0)) { + bes2600_err(BES2600_DBG_SDIO, "request_irq or irq_wake failed with %d\n", ret); + return ret; + } +#endif + + pdata->wlan_bt_hostwake_registered = true; + pdata->wakeup_source = false; + + return ret; +} + +static void bes2600_wlan_bt_hostwake_unregister(void) +{ + struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + + int ret = 0; +#if defined(PLAT_ALLWINNER) + int irq = sunxi_wlan_get_oob_irq(); +#elif defined(PLAT_ROCKCHIP) + int irq = rockchip_wifi_get_oob_irq(); +#endif + + if (pdata->wlan_bt_hostwake_registered == false) + return; + + pdata->wlan_bt_hostwake_registered = false; +#if defined(PLAT_ALLWINNER) || defined(PLAT_ROCKCHIP) + ret = disable_irq_wake(irq); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "disable_irq_wake failed with %d\n", ret); + } + free_irq(irq, pdata); +#endif +} + +static int bes2600_gpio_wakeup_ap_config(struct sbus_priv *self) +{ + u8 wakeup_cfg = 0; + int ret = 0, irq_flags = 0, irq = 0; + + if (!bes2600_chrdev_is_signal_mode()) + return 0; + +#if defined(PLAT_ALLWINNER) + irq_flags = sunxi_wlan_get_oob_irq_flags(); + irq = sunxi_wlan_get_oob_irq(); +#elif defined(PLAT_ROCKCHIP) + irq_flags = rockchip_wifi_get_oob_irq_flag(); + irq = rockchip_wifi_get_oob_irq(); +#endif + + if (irq_flags & IRQF_TRIGGER_HIGH) { + wakeup_cfg = BES_AP_WAKEUP_GPIO_HIGH | BES_AP_WAKEUP_CFG_VALID; + } else if (irq_flags & IRQF_TRIGGER_LOW) { + wakeup_cfg = BES_AP_WAKEUP_GPIO_LOW | BES_AP_WAKEUP_CFG_VALID; + } else if (irq_flags & IRQF_TRIGGER_RISING) { + wakeup_cfg = BES_AP_WAKEUP_GPIO_RISE | BES_AP_WAKEUP_CFG_VALID; + } else if (irq_flags & IRQF_TRIGGER_FALLING) { + wakeup_cfg = BES_AP_WAKEUP_GPIO_FALL | BES_AP_WAKEUP_CFG_VALID; + } + + if (wakeup_cfg & BES_AP_WAKEUP_CFG_VALID) + wakeup_cfg |= (BES_AP_WAKEUP_TYPE_GPIO << BES_AP_WAKEUP_TYPE_SHIFT); + + bes2600_info(BES2600_DBG_SDIO, "%s config:%x\n", __func__, wakeup_cfg); + + sdio_claim_host(self->func); + sdio_writeb(self->func, wakeup_cfg, BES_AP_WAKEUP_REG_ID, &ret); + if (!ret) { + sdio_writeb(self->func, 0, BES_HOST_INT_REG_ID + 1, &ret); + } + if (!ret) { + sdio_writeb(self->func, (BES_HOST_INT | BES_AP_WAKEUP_CFG), BES_HOST_INT_REG_ID, &ret); + } + sdio_release_host(self->func); + if (ret) { + bes2600_err(BES2600_DBG_SDIO, "%s failed:%d\n", __func__, ret); + free_irq(irq, &bes_sdio_plat_data); + return ret; + } + + return 0; +} +#endif + +int bes2600_sdio_prepare(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sbus_priv *self = sdio_get_drvdata(func); + + bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num); + + if (func->num > 1) + return 0; + + if(bes2600_sdio_sbus_ops.gpio_wake) + bes2600_sdio_sbus_ops.gpio_wake(self, GPIO_WAKE_FLAG_HOST_SUSPEND); + + return 0; +} + +static int bes2600_sdio_suspend(struct device *dev) +{ + int ret; + struct sdio_func *func = dev_to_sdio_func(dev); + struct sbus_priv *self = sdio_get_drvdata(func); + + bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num); + if (func->num > 1) + return 0; + +#ifndef CONFIG_BES2600_WOWLAN + if(bes2600_chrdev_check_system_close() == false) + return -EBUSY; +#endif + + /* Notify SDIO that BES2600 will remain powered during suspend */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + bes2600_err(BES2600_DBG_PM, "Error setting SDIO pm flags: %i\n", ret); + return ret; + } + + if (bes2600_chrdev_is_bt_opened() == true) { + if ((ret = bes2600_sdio_deactive(self, SUBSYSTEM_BT_LP))) { + bes2600_err(BES2600_DBG_PM, "bt sleep in suspend failed:%d.\n", ret); + return ret; + } + } + +#ifdef BES2600_GPIO_WAKEUP_AP + return bes2600_wlan_bt_hostwake_register(); +#endif + + return 0; +} + +static int bes2600_sdio_suspend_noirq(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sbus_priv *self = sdio_get_drvdata(func); + + bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num); + + if (func->num > 1) + return 0; + + if(self->core && + (work_pending(&self->rx_work) || atomic_read(&self->core->bh_rx))) { + bes2600_info(BES2600_DBG_SDIO, "%s: Suspend interrupted.\n", __func__); + return -EAGAIN; + } + + if(bes2600_sdio_sbus_ops.gpio_sleep) + bes2600_sdio_sbus_ops.gpio_sleep(self, GPIO_WAKE_FLAG_HOST_SUSPEND); + + if (self->retune_protected == true) + bes2600_warn(BES2600_DBG_SDIO, "retune is closed while ap sleep.\n"); + + return 0; +} + +int bes2600_sdio_resume_noirq(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sbus_priv *self = sdio_get_drvdata(func); + + bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num); + + if (func->num > 1) + return 0; + + if(bes2600_sdio_sbus_ops.gpio_wake) + bes2600_sdio_sbus_ops.gpio_wake(self, GPIO_WAKE_FLAG_HOST_RESUME); + + return 0; +} + +static int bes2600_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + + bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num); + + if (func->num > 1) + return 0; + +#ifdef BES2600_GPIO_WAKEUP_AP + bes2600_wlan_bt_hostwake_unregister(); +#endif + + return 0; +} + +static void bes2600_sdio_complete(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sbus_priv *self = sdio_get_drvdata(func); + + bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num); + + if (func->num > 1) + return; + + /* wakeup bt if bt is on */ + bes2600_chrdev_wakeup_bt(); + + /* clear resume gpio wake flag */ + if(bes2600_sdio_sbus_ops.gpio_sleep) + bes2600_sdio_sbus_ops.gpio_sleep(self, GPIO_WAKE_FLAG_HOST_RESUME); +} + +static const struct dev_pm_ops bes2600_pm_ops = { + .prepare = bes2600_sdio_prepare, + .suspend = bes2600_sdio_suspend, + .suspend_noirq = bes2600_sdio_suspend_noirq, + .resume_noirq = bes2600_sdio_resume_noirq, + .resume = bes2600_sdio_resume, + .complete = bes2600_sdio_complete, +}; + +static struct sdio_driver sdio_driver = { + .name = "bes2600_wlan", + .id_table = bes2600_sdio_ids, + .probe = bes2600_sdio_probe, + .remove = bes2600_sdio_disconnect, + .drv = { + .pm = &bes2600_pm_ops, + } +}; + +/* Init Module function -> Called by insmod */ +static int __init bes2600_sdio_init(void) +{ + int ret; + const struct bes2600_platform_data_sdio *pdata = NULL; + + bes2600_info(BES2600_DBG_SDIO, "------Driver: bes2600.ko version :%s\n", BES2600_DRV_VERSION); + + bes2600_chrdev_update_signal_mode(); + bes2600_dbg(BES2600_DBG_SDIO, "%s type:%d sig_mode:%d\n", __func__, + bes2600_chrdev_get_fw_type(), bes2600_chrdev_is_signal_mode()); + + if ((ret = bes2600_platform_data_init())) + goto exit; + + pdata = bes2600_get_platform_data(); + + ret = bes2600_chrdev_init(&bes2600_sdio_sbus_ops); + if(ret) + goto err_chardev; + + ret = bes2600_sdio_on(pdata); + if (ret) + goto err_on; + +#ifdef PLAT_ALLWINNER + mdelay(10); + bes2600_chrdev_start_bus_probe(); + //sw_mci_rescan_card(AW_SDIOID, 1); + sunxi_mmc_rescan_card(sunxi_wlan_get_bus_index()); + bes2600_info(BES2600_DBG_SDIO, "%s: power up, rescan card.\n", __FUNCTION__); +#endif + + ret = sdio_register_driver(&sdio_driver); + if (ret) + goto err_reg; + + return 0; + +err_reg: + bes2600_sdio_off(pdata); +err_on: + bes2600_chrdev_free(); +err_chardev: + bes2600_platform_data_deinit(); +exit: + return ret; +} + +/* Called at Driver Unloading */ +static void __exit bes2600_sdio_exit(void) +{ + const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); + struct sbus_priv *priv = bes2600_chrdev_get_sbus_priv_data(); + bes2600_info(BES2600_DBG_SDIO, "%s called\n", __func__); + + sdio_unregister_driver(&sdio_driver); + bes2600_chrdev_free(); + if(!priv) { + bes2600_sdio_off(pdata); + } + bes2600_platform_data_deinit(); +} + +module_init(bes2600_sdio_init); +module_exit(bes2600_sdio_exit); diff --git a/drivers/staging/bes2600/bes2600_spi.c b/drivers/staging/bes2600/bes2600_spi.c new file mode 100644 index 000000000000..a818bc9f2106 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_spi.c @@ -0,0 +1,1803 @@ +/* + * Mac80211 SPI driver for BES2600 device + * + * Based on bes2600_sdio.c + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bes2600.h" +#include "sbus.h" +#include "wsm.h" +#include "bes2600_plat.h" +#include "hwio.h" + +#include +#include +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" + +#if defined(FW_DOWNLOAD_BY_UART) +extern int bes2600_load_firmware_uart(struct bes2600_common *hw_priv); +#endif + +MODULE_AUTHOR("Solomon Peachy "); +MODULE_DESCRIPTION("mac80211 BES2600 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:bes2600_wlan_spi"); + +/* #define SPI_DEBUG */ + +struct sbus_priv { + struct spi_device *func; + struct bes2600_common *core; + const struct bes2600_platform_data_spi *pdata; + struct device *dev; + spinlock_t lock; /* Serialize all bus operations */ + wait_queue_head_t wq; + int claimed; + struct spi_message m; + struct spi_transfer t_cmd; + struct spi_transfer t_data; + u8 *tx_dummy_buf; + u8 *rx_dummy_buf; + u8 *rx_offline_packets; + struct sk_buff_head rx_queue; + int gpio_irq; + u32 lmac_ptr_pool[BES_LMAC_BUF_NUMS]; + u32 lmac_ptr_top; + u32 lmac_ptr; + u32 lmac_addr_min; + u32 lmac_addr_max; + u32 packets_upload; + bool spi_active; + long unsigned int gpio_wakup_flags; +}; + +/* Notes on byte ordering: + LE: B0 B1 B2 B3 + BE: B3 B2 B1 B0 + + Hardware expects 32-bit data to be written as 16-bit BE words: + + B1 B0 B3 B2 +*/ + +static int bes2600_spi_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + int ret; + //u32 cfg_addr, cfg_val; + u32 reg_addr, done_addr, done_val; + struct spi_transfer *t_cmd; + struct spi_transfer *t_data; + struct spi_message *m; + + bes2600_dbg(BES2600_DBG_SPI, "%s, count=%d,addr=%x\n", __func__, count, addr); + if (!dst || (count & 0x3) || ((addr != BES_TX_DATA_ADDR) && (addr != BES_CALI_DATA_ADDR) && (addr < BES_LMAC_BUF_DESC + || addr > BES_LMAC_BUF_DESC + BES_LMAC_BUF_NUMS * 4))) { + bes2600_err(BES2600_DBG_SPI, "%s,%d %x:%p:%d", __func__, __LINE__, addr, dst, count); + return -EINVAL; + } + + BUG_ON(!self); + BUG_ON(!self->tx_dummy_buf); + BUG_ON(!self->rx_dummy_buf); + t_cmd = &self->t_cmd; + t_data = &self->t_data; + m = &self->m; + + //cfg_addr = SPI_WR_ADDR(SPI_RD_CFG_REG_ID); + //cfg_val = SPI_CONTINUOUS_CFG_VAL; + reg_addr = SPI_RD_ADDR(addr); + done_addr = SPI_WR_ADDR(BES_HOST_INT_REG_ID); + done_val = BES_HOST_INT_RD_DONE; + +#if defined(__LITTLE_ENDIAN) + if (likely(self->func->bits_per_word == 8)) { + //cfg_addr = swab32(cfg_addr); + //cfg_val = swab32(cfg_val); + reg_addr = swab32(reg_addr); + done_addr = swab32(done_addr); + done_val = swab32(done_val); + } +#endif + + memset(m, 0, sizeof(*m)); + memset(t_cmd, 0, sizeof(*t_cmd)); + memset(t_data, 0, sizeof(*t_data)); + //memcpy(self->tx_dummy_buf, &cfg_addr, 4); + //memcpy(self->tx_dummy_buf + 4, &cfg_val, 4); + memcpy(self->tx_dummy_buf + 8, ®_addr, 4); + memcpy(self->tx_dummy_buf + 16 + count, &done_addr, 4); + memcpy(self->tx_dummy_buf + 20 + count, &done_val, 4); + //t_cmd->tx_buf = self->tx_dummy_buf; + //t_cmd->rx_buf = self->rx_dummy_buf; + //t_cmd->len = 4 + 4; + //spi_message_init(m); + //spi_message_add_tail(t_cmd, m); + //ret = spi_sync(self->func, m); + //if (ret) { + //bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + //goto exit; + //} + + t_cmd->tx_buf = &self->tx_dummy_buf[8]; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = 4 + 4; + t_cmd->speed_hz = self->func->max_speed_hz; + t_data->tx_buf = &self->tx_dummy_buf[16]; + t_data->rx_buf = dst; + t_data->len = count; + t_data->speed_hz = self->func->max_speed_hz; + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + spi_message_add_tail(t_data, m); + + ret = spi_sync(self->func, m); + if (!ret) { +#if 0 + bes2600_dbg(BES2600_DBG_SPI, "READ LENGTH:%d,%d,%u\n", m->actual_length, count, ++packets_upload); + bes2600_dbg_dump(BES2600_DBG_SPI, "read_data:", dst, 8); + if (PACKET_COUNT(ctrl_reg) >= 2) + bes2600_dbg_dump(BES2600_DBG_SPI, "read_data1:", self->rx_offline_packets[0], 8); + if (PACKET_COUNT(ctrl_reg) >= 3) + bes2600_dbg_dump(BES2600_DBG_SPI, "read_data2:", self->rx_offline_packets[1], 8); +#endif + } else { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + goto exit; + } + + if (addr != BES_TX_DATA_ADDR && addr != BES_CALI_DATA_ADDR) { +#if defined(__LITTLE_ENDIAN) + if (likely(self->func->bits_per_word == 8)) { + u32 *buf_u32 = (u32 *)dst; + int i; + for(i = 0; i < (count >> 2); i++) { + buf_u32[i] = swab32(buf_u32[i]); + } + } +#endif + return ret; + } + + t_cmd->tx_buf = &self->tx_dummy_buf[count + 16]; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = 4 + 4; + t_cmd->speed_hz = self->func->max_speed_hz; + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + ret = spi_sync(self->func, m); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + } + self->packets_upload ++; + + bes2600_dbg_dump(BES2600_DBG_SPI, "read_done:", &self->tx_dummy_buf[count + 16], 8); +exit: + return ret; +} + +static int bes2600_spi_addr_update(struct sbus_priv *self); + +static int bes2600_spi_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + int ret, i; + u32 reg_addr, reg_use, src_pos = 0, buf_pos = 4; + u32 done_addr, done_val; + u8 *src_u8; + u32 *src_u32, *buf_u32; + struct spi_transfer *t_cmd; + struct spi_transfer *t_data; + struct spi_message *m; + + if (!src || count <= 0 || ((!addr) && (count >= 1632))) { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%p:%d\n", __func__, __LINE__, src, count); + return -EINVAL; + } + + BUG_ON(!self); + BUG_ON(!self->tx_dummy_buf); + BUG_ON(!self->rx_dummy_buf); + m = &self->m; + t_cmd = &self->t_cmd; + t_data = &self->t_data; + + if (likely(!addr)) { + if (bes2600_chrdev_is_signal_mode()) { + ret = bes2600_spi_addr_update(self); + if (WARN_ON(ret)) { + return ret; + } + reg_use = self->lmac_ptr_pool[self->lmac_ptr & (BES_LMAC_BUF_NUMS - 1)]; + if (WARN_ON(reg_use > self->lmac_addr_max || reg_use < self->lmac_addr_min)) { + bes2600_err(BES2600_DBG_SPI, "%s, addr err:%x,%x,%x\n", __func__, reg_use, self->lmac_addr_max, self->lmac_addr_min); + return -EIO; + } + self->lmac_ptr++; + } else { + reg_use = BES_MISC_DATA_ADDR; + } + } else { + reg_use = addr; + } + + reg_addr = SPI_WR_ADDR(reg_use); + if (addr != BES_MISC_DATA_ADDR) { + done_val = BES_HOST_INT_WR_DONE; + done_addr = SPI_WR_ADDR(BES_HOST_INT_REG_ID); + } else { + done_val = BES_MISC_DATA_DONE; + done_addr = SPI_WR_ADDR(BES_HOST_SUBINT_REG_ID); + } + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + bes2600_dbg(BES2600_DBG_SPI, "write_addr:%x(%u)\n", reg_use, self->lmac_ptr); + bes2600_dbg_dump(BES2600_DBG_SPI, "wirte_data:", src, 8); + if (likely(self->func->bits_per_word == 8)) + { + reg_addr = swab32(reg_addr); + done_addr = swab32(done_addr); + done_val = swab32(done_val); + buf_pos = 1; + src_pos = 0; + src_u32 = (u32 *)src; + buf_u32 = (u32 *)self->tx_dummy_buf; + for (i = 0; i < (count >> 2); i++) { + buf_u32[buf_pos] = swab32(src_u32[src_pos]); + buf_pos ++; + src_pos ++; + } + + buf_pos = buf_pos << 2; + src_pos = src_pos << 2; + + if (count & 0x3) { + + src_u8 = (u8 *)src; + if ((count % 4) == 1) { + self->tx_dummy_buf[buf_pos ++] = 0; + self->tx_dummy_buf[buf_pos ++] = 0; + self->tx_dummy_buf[buf_pos ++] = 0; + self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos ++]; + } else if ((count % 4) == 2) { + self->tx_dummy_buf[buf_pos ++] = 0; + self->tx_dummy_buf[buf_pos ++] = 0; + self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos + 1]; + self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos]; + src_pos += 2; + } else { + self->tx_dummy_buf[buf_pos ++] = 0; + self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos + 2]; + self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos + 1]; + self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos]; + src_pos += 3; + } + } + } + bes2600_dbg(BES2600_DBG_SPI, "transver %u,%u(%d)\n", buf_pos, src_pos, count); +#endif + + bes2600_dbg(BES2600_DBG_SPI, "WRITE %d:%x(%x)\n", count, reg_use, reg_addr); + + memset(t_cmd, 0, sizeof(*t_cmd)); + memcpy(self->tx_dummy_buf, ®_addr, 4); + memcpy(&self->tx_dummy_buf[buf_pos], &done_addr, 4); + memcpy(&self->tx_dummy_buf[buf_pos + 4], &done_val, 4); + t_cmd->tx_buf = self->tx_dummy_buf; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = buf_pos; + t_cmd->speed_hz = self->func->max_speed_hz; + + bes2600_dbg_dump(BES2600_DBG_SPI, "WRITE:", (u8 *)t_cmd->tx_buf, t_cmd->len); + + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + if (!src_pos) { + memset(t_data, 0, sizeof(*t_data)); + t_data->tx_buf = src; + t_data->rx_buf = self->rx_dummy_buf; + t_data->len = count; + t_data->speed_hz = self->func->max_speed_hz; + spi_message_add_tail(t_data, m); + } + ret = spi_sync(self->func, m); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + } + + if ((!addr) || (addr == BES_MISC_DATA_ADDR)) { + t_cmd->tx_buf = &self->tx_dummy_buf[buf_pos]; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = 8; + t_cmd->speed_hz = self->func->max_speed_hz; + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + ret = spi_sync(self->func, m); + if (ret) + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d.\n", __func__, __LINE__, ret); + } + + if (addr == BES_MISC_DATA_ADDR) { + done_val = BES_HOST_INT_SUBINT; + done_addr = SPI_WR_ADDR(BES_HOST_INT_REG_ID); + done_val = swab32(done_val); + done_addr = swab32(done_addr); + memcpy(&self->tx_dummy_buf[buf_pos], &done_addr, 4); + memcpy(&self->tx_dummy_buf[buf_pos + 4], &done_val, 4); + t_cmd->tx_buf = &self->tx_dummy_buf[buf_pos]; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = 8; + t_cmd->speed_hz = self->func->max_speed_hz; + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + ret = spi_sync(self->func, m); + if (ret) + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d.\n", __func__, __LINE__, ret); + } + + //print_hex_dump(KERN_NOTICE, "write_done ", 0, 16, 1, &self->tx_dummy_buf[buf_pos], 8, false); + //temp_test(self); + + return ret; +} + +static void bes2600_spi_lock(struct sbus_priv *self) +{ + unsigned long flags; + + DECLARE_WAITQUEUE(wait, current); + + might_sleep(); + + add_wait_queue(&self->wq, &wait); + spin_lock_irqsave(&self->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!self->claimed) + break; + spin_unlock_irqrestore(&self->lock, flags); + schedule(); + spin_lock_irqsave(&self->lock, flags); + } + set_current_state(TASK_RUNNING); + self->claimed = 1; + spin_unlock_irqrestore(&self->lock, flags); + remove_wait_queue(&self->wq, &wait); + + return; +} + +static void bes2600_spi_unlock(struct sbus_priv *self) +{ + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + self->claimed = 0; + spin_unlock_irqrestore(&self->lock, flags); + wake_up(&self->wq); + + return; +} + +static irqreturn_t bes2600_spi_irq_handler(int irq, void *dev_id) +{ + struct sbus_priv *self = dev_id; + + if (self->core) { + //bes2600_spi_lock(self); + bes2600_irq_handler(self->core); + //bes2600_spi_unlock(self); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +struct bes2600_platform_data_spi *bes2600_get_platform_data(void); +static int bes2600_spi_irq_subscribe(struct sbus_priv *self, sbus_irq_handler handler, + void *priv) +{ + int ret, irq; + + bes2600_info(BES2600_DBG_SPI, "SW IRQ subscribe\n"); + + if (self->pdata && gpio_is_valid(self->pdata->irq_gpio)) { + self->gpio_irq = gpio_to_irq(self->pdata->irq_gpio); + bes2600_info(BES2600_DBG_SPI, "gpio for irq detected:%d,%d\n", self->pdata->irq_gpio, self->gpio_irq); + irq = self->gpio_irq; + } else { + irq = self->func->irq; + } + + ret = request_threaded_irq(irq, NULL, + bes2600_spi_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "bes2600_wlan_irq", self); + + if (WARN_ON(ret < 0)) + goto exit; + + ret = enable_irq_wake(irq); + if (WARN_ON(ret)) + goto free_irq; + + return 0; + +free_irq: + free_irq(irq, self); + +exit: + return ret; +} + +static int bes2600_spi_irq_unsubscribe(struct sbus_priv *self) +{ + int ret = 0, irq; + + bes2600_info(BES2600_DBG_SPI, "SW IRQ unsubscribe\n"); + + if (!IS_ERR_VALUE((uintptr_t)self->gpio_irq)) + irq = self->gpio_irq; + else + irq = self->func->irq; + + disable_irq_wake(irq); + free_irq(irq, self); + + return ret; +} + +static int bes2600_spi_off(const struct bes2600_platform_data_spi *pdata) +{ + bes2600_info(BES2600_DBG_SPI, "%s enter\n", __func__); + + if (pdata == NULL) + return 0; + +#if defined(BES2600_INDEPENDENT_EVB) || defined(BES2600_INTEGRATED_MODULE_V3) + if (gpio_is_valid(pdata->powerup)) { + bes2600_info(BES2600_DBG_SPI, "bes2600 powerdown.\n"); + gpio_direction_output(pdata->powerup, 0); + } +#endif + + return 0; +} + +static int bes2600_spi_on(const struct bes2600_platform_data_spi *pdata) +{ + bes2600_info(BES2600_DBG_SPI, "%s enter\n", __func__); + + if (pdata == NULL) + return 0; + +#if defined(BES2600_INDEPENDENT_EVB) || defined(BES2600_INTEGRATED_MODULE_V3) + if (gpio_is_valid(pdata->powerup)) { + bes2600_info(BES2600_DBG_SPI, "bes2600 powerup.\n"); + gpio_direction_output(pdata->powerup, 1); + } +#endif + + return 0; +} + +static size_t bes2600_spi_align_size(struct sbus_priv *self, size_t size) +{ +#if 0 + return ((size + 3) & (~0x3)); +#else + return size; +#endif +} + +int bes2600_spi_set_block_size(struct sbus_priv *self, size_t size) +{ + return 0; +} + +int bes2600_spi_send(struct bes2600_common *ar, u8 pipe, u32 len, u8 *buf) +{ + return 0; +} + +void * bes2600_spi_read(struct bes2600_common *ar) +{ + return 0; +} + +static int bes2600_spi_reg_write(struct sbus_priv *self, u32 reg, + const void *src, int count); +static int bes2600_spi_init(struct sbus_priv *self, struct bes2600_common *ar) +{ + int ret = 0; + //u32 cfg_val; + + if (self->tx_dummy_buf) { + bes2600_err(BES2600_DBG_SPI, "bes2600 sbus has been initialized ???\n"); + return -1; + } + + /* 4byte for read cfg_addr + * 4byte for read cfg_val + * 4byte for read addr + * 4byte for status followed read addr + * 1632byte for data + * 4byte for done addr + * 4byte for done val + */ +#if 0 + self->tx_dummy_buf = kmalloc(1632 + 24, GFP_KERNEL); + if (!self->tx_dummy_buf) + return -ENOMEM; + + self->rx_dummy_buf = kmalloc(1632 + 24, GFP_KERNEL); + if (!self->rx_dummy_buf) { + ret = -ENOMEM; + goto free_tx; + } +#else + self->tx_dummy_buf = (u8 *)__get_dma_pages(GFP_KERNEL, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM)); + if (!self->tx_dummy_buf) + return -ENOMEM; + self->rx_dummy_buf = (u8 *)__get_dma_pages(GFP_KERNEL, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM)); + if (!self->rx_dummy_buf) + goto free_tx; + bes2600_info(BES2600_DBG_SPI, "%s, buf_addr:%p:%p(%d)\n", __func__, + self->tx_dummy_buf, self->rx_dummy_buf, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM)); +#endif + self->core = ar; + + self->lmac_ptr_top = 0; + self->lmac_ptr = 0; + self->lmac_addr_min = 0; + self->lmac_addr_max = 0; + self->packets_upload = 0; + + skb_queue_head_init(&self->rx_queue); + +#if 0 + cfg_val = SPI_CONTINUOUS_CFG_VAL; + if ((ret = bes2600_spi_reg_write(self, SPI_RD_CFG_REG_ID, &cfg_val, 4))) { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + goto free_rx; + } +#endif + + return 0; + +#if 0 +free_rx: + free_pages((unsigned long)self->rx_dummy_buf, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM)); +#endif + +free_tx: + free_pages((unsigned long)self->tx_dummy_buf, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM)); + + return ret; +} + +/* bes spi slave regs can only be accessed as DWORD(32-bit) + * if BYTE or WORD(16-bit) reg wants to be accessed, + * please extract form a DWORD read or write operation and this is not implemented yet + */ +static int bes2600_spi_reg_read(struct sbus_priv *self, u32 reg, + void *dst, int count) +{ + int ret = 0; + u32 reg_addr, cfg_val; + //u32 cfg_addr; + struct spi_message *m; + struct spi_transfer *t_cmd; + + if (!dst || count != 4) { + bes2600_err(BES2600_DBG_SPI, "%s,%d %p:%d\n", __func__, __LINE__, dst, count); + return -EINVAL; + } + BUG_ON(!self); + BUG_ON(!self->tx_dummy_buf); + BUG_ON(!self->rx_dummy_buf); + + m = &self->m; + t_cmd = &self->t_cmd; + + //cfg_addr = SPI_WR_ADDR(SPI_RD_CFG_REG_ID); + //cfg_val = SPI_NCONTINUOUS_CFG_VAL; + reg_addr = SPI_RD_ADDR(reg); + //bes2600_info(BES2600_DBG_SPI, "TX %x:%x:%x(%x)\n", cfg_addr, cfg_val, reg_addr, reg); + +#if defined(__LITTLE_ENDIAN) + if (likely(self->func->bits_per_word == 8)) { + //cfg_addr = swab32(cfg_addr); + //cfg_val = swab32(cfg_val); + reg_addr = swab32(reg_addr); + } +#endif + + memset(t_cmd, 0, sizeof(*t_cmd)); + //memcpy(self->tx_dummy_buf, &cfg_addr, 4); + //memcpy(self->tx_dummy_buf + 4, &cfg_val, 4); + memcpy(self->tx_dummy_buf + 8, ®_addr, 4); + //t_cmd->tx_buf = &self->tx_dummy_buf; + //t_cmd->rx_buf = self->rx_dummy_buf; + //t_cmd->len = 4 + 4; + //spi_message_init(m); + //spi_message_add_tail(t_cmd, m); + //ret = spi_sync(self->func, m); + //if (ret) { + //bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + //goto exit; + //} + + t_cmd->tx_buf = &self->tx_dummy_buf[8]; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = 4 + 4 + 4; + t_cmd->speed_hz = self->func->max_speed_hz; + + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + ret = spi_sync(self->func, m); + if (!ret){ + + bes2600_dbg_dump(BES2600_DBG_SPI, "READ:", (u8 *)t_cmd->rx_buf, t_cmd->len); + cfg_val = *(u32 *)(t_cmd->rx_buf + 8); + cfg_val = swab32(cfg_val); + memcpy(dst, &cfg_val, 4); + //bes2600_info(BES2600_DBG_SPI, "reg_read val:%x\n", cfg_val); + } + +//exit: + return ret; +} + +static int bes2600_spi_reg_write(struct sbus_priv *self, u32 reg, + const void *src, int count) +{ + int ret = 0; + u32 reg_addr, wr_val; + struct spi_message *m; + struct spi_transfer *t_cmd; + + if (!src || count != 4) { + bes2600_err(BES2600_DBG_SPI, "%s,%d %p:%d\n", __func__, __LINE__, src, count); + return -EINVAL; + } + BUG_ON(!self); + BUG_ON(!self->tx_dummy_buf); + BUG_ON(!self->rx_dummy_buf); + m = &self->m; + t_cmd = &self->t_cmd; + + reg_addr = SPI_WR_ADDR(reg); + memcpy(&wr_val, src, 4); + //bes2600_info(BES2600_DBG_SPI, "TX_CMD %08x(%08x):%08x\n", reg_addr, reg, wr_val); + +#if defined(__LITTLE_ENDIAN) + if (likely(self->func->bits_per_word == 8)) { + reg_addr = swab32(reg_addr); + wr_val = swab32(wr_val); + //bes2600_info(BES2600_DBG_SPI, "TRANSVERT_TX_CMD %08x:%08x\n", reg_addr, wr_val); + } +#endif + + memset(m, 0, sizeof(*m)); + memset(t_cmd, 0, sizeof(*t_cmd)); + memcpy(self->tx_dummy_buf, ®_addr, 4); + memcpy(self->tx_dummy_buf + 4, &wr_val, 4); + + t_cmd->tx_buf = self->tx_dummy_buf; + t_cmd->rx_buf = self->rx_dummy_buf; + t_cmd->len = 4 + 4; + t_cmd->speed_hz = self->func->max_speed_hz; + + bes2600_dbg_dump(BES2600_DBG_SPI, "WRITE:", (u8 *)t_cmd->tx_buf,t_cmd->len); + + spi_message_init(m); + spi_message_add_tail(t_cmd, m); + ret = spi_sync(self->func, m); + if (!ret){ + //bes2600_info(BES2600_DBG_SPI, "reg_write:%x", *(u32 *)src); + } else { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d", __func__, __LINE__, ret); + } + return ret; +} + +static int bes2600_spi_addr_update(struct sbus_priv *self) +{ + int ret; + u32 pos; + u32 diff = (u32)(self->lmac_ptr_top - self->lmac_ptr); + + if (diff > 0 && diff < BES_LMAC_BUF_NUMS) + return 0; + + if ((ret = bes2600_spi_reg_read(self, BES_LMAC_BUF_TOTAL, &self->lmac_ptr_top, 4))) { + return ret; + } + + diff = (u32)(self->lmac_ptr_top - self->lmac_ptr); + BUG_ON(diff > BES_LMAC_BUF_NUMS); + + pos = self->lmac_ptr & (BES_LMAC_BUF_NUMS - 1); + //bes2600_info(BES2600_DBG_SPI, "%s, %u %u %u\n", __func__, diff, pos, self->lmac_ptr_top); + if (BES_LMAC_BUF_NUMS - pos >= diff) + ret = bes2600_spi_memcpy_fromio(self, BES_LMAC_BUF_DESC + pos * 4, &self->lmac_ptr_pool[pos], diff << 2); + else { + ret = bes2600_spi_memcpy_fromio(self, BES_LMAC_BUF_DESC, self->lmac_ptr_pool, BES_LMAC_BUF_NUMS << 2); + } + + if (unlikely(!self->lmac_addr_min)) { + self->lmac_addr_min = self->lmac_ptr_pool[0]; + /* temporary workaround */ + self->lmac_addr_max = self->lmac_ptr_pool[self->lmac_ptr_top - 1] + 1632 + 1632; + } + + return ret; +} + +static int bes2600_spi_packets_check(u32 ctrl_reg, u8 *packets) +{ + int i; + u32 single, total_cal = 0; + u32 packets_length; + u32 *buf_u32; + + /* bit 31-28 indicate count of packets */ + u32 packets_cnt = PACKET_COUNT_V2(ctrl_reg); + if (WARN_ON(packets_cnt > MAX_SEND_PACKETS_NUM)) + return -200; + + /* bit 27-0 indicate totoal length of packets */ + packets_length = PACKET_TOTAL_LEN_V2(ctrl_reg); + + /* first 32-bit: addr in mcu; + * second 32-bit: packet length; + * next: data + */ + for (i = 0; i < packets_cnt; i++) { + buf_u32 = (u32 *)(packets + total_cal); + //bes2600_info(BES2600_DBG_SPI, "%s, %x,%x,%x\n", __func__, buf_u32[0], buf_u32[1], ctrl_reg); + single = buf_u32[1]; + if (WARN_ON(single > 1632)) { + return -201; + } + total_cal += single; + total_cal += 8; + } + //bes2600_info(BES2600_DBG_SPI, "%s, %d,%u,%u\n", __func__, packets_cnt, packets_length, total_cal); + + if (WARN_ON(packets_length != total_cal)) { + return -202; + } + + return 0; +} + +static int bes2600_spi_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 *data) +{ + int i; + u32 packets_cnt = PACKET_COUNT_V2(ctrl_reg); + u32 packet_len, pos = 0; + struct sk_buff *skb; + for (i = 0; i < packets_cnt; i++) { + packet_len = *(u32 *)&(data[pos + 4]); + skb = dev_alloc_skb(packet_len); + if (WARN_ON(!skb)) { + return -ENOMEM; + } + skb_trim(skb, 0); + skb_put(skb, packet_len); + memcpy(skb->data, &data[pos + 8], packet_len); + //bes2600_info(BES2600_DBG_SPI, "%s, %d,%d\n", __func__, packet_len, skb->len); + skb_queue_tail(&self->rx_queue, skb); + pos += packet_len; + pos += 8; + } + return 0; +} + +static void *bes2600_spi_pipe_read(struct sbus_priv *self) +{ + int ret; + u32 ctrl_reg; + int total_len; + u8 *buf = &self->rx_dummy_buf[8]; + struct sk_buff *skb; + + skb = skb_dequeue(&self->rx_queue); + if (skb != NULL) + return skb; + + bes2600_spi_lock(self); + + if (unlikely(self->spi_active == false)) { + goto null_exit; + } + + ret = bes2600_spi_reg_read(self, BES_TX_CTRL_REG_ID, &ctrl_reg, 4); + if (WARN_ON(ret)) + goto null_exit; + + if (!ctrl_reg) + goto null_exit; + + total_len = PACKET_TOTAL_LEN_V2(ctrl_reg); + ret = bes2600_spi_memcpy_fromio(self, BES_TX_DATA_ADDR, buf, total_len); + if (WARN_ON(ret)) + goto null_exit; + + if ((ret = bes2600_spi_packets_check(ctrl_reg, buf))) + goto null_exit; + + if ((ret = bes2600_spi_extract_packets(self, ctrl_reg, buf))) + goto null_exit; + + bes2600_spi_unlock(self); + + return skb_dequeue(&self->rx_queue); + +null_exit: + bes2600_spi_unlock(self); + return NULL; +} + +void bes2600_sbus_dump(void *priv) +{ + struct sbus_priv *sbus_priv = (struct sbus_priv *)priv; + bes2600_info(BES2600_DBG_SPI, "%s, upload=%u\n", __func__, sbus_priv->packets_upload); +} + +#if defined(CONFIG_BES2600_SPI_THROUGHPUT_TEST) +#include +static struct proc_dir_entry *bes_wlan_dir; +#endif + +#ifdef CONFIG_BES2600_SPI_THROUGHPUT_TEST +static struct proc_dir_entry *spi_throughput_entry; +static struct spi_throughput_struct { + struct task_struct *thread; + wait_queue_head_t test_wq; + unsigned long test_count, test_packet_size; + unsigned long read_packets, irq_enters; + unsigned long average_time, max_time, min_time; + unsigned long total_time; + atomic_t rx; + void *bus_data; + char read_buf[512]; +} *spi_throughput_data; +static int spi_throughput_test(void *arg); +static const struct sbus_ops bes2600_spi_sbus_ops; + +static int spi_throughput_open(struct inode *inode, struct file *filp) +{ + bes2600_info(BES2600_DBG_SPI, "%s\n", __func__); + return 0; +} + +static int spi_throughput_close(struct inode *inode, struct file *filp) +{ + bes2600_info(BES2600_DBG_SPI, "%s\n", __func__); + return 0; +} + +static ssize_t spi_throughput_read(struct file *filp, char __user *buf, + size_t size, loff_t *ppos) +{ + bes2600_info(BES2600_DBG_SPI, "%s, %ld\n", __func__, size); + return 0; +} + +static ssize_t spi_throughput_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + char data[512]; + char cnt_delimiter[] = "count="; + char size_delimiter[] = "size="; + char *cnt_p, *size_p; + unsigned long len; + char *end; + memset(data, 0, 512); + len = copy_from_user(data, buf, count < 511 ? count : 511); + bes2600_info(BES2600_DBG_SPI, "%s, %s\n", __func__, data); + if (strncmp(data, "start", 5) == 0) { + cnt_p = strstr(data, cnt_delimiter); + size_p = strstr(data, size_delimiter); + if (!cnt_p || !size_p) + goto exit; + bes2600_info(BES2600_DBG_SPI, "%s\n", cnt_p); + bes2600_info(BES2600_DBG_SPI, "%s\n", size_p); + if (cnt_p > (data + 511) || size_p > (data + 511)) + goto exit; + spi_throughput_data->test_count = simple_strtoul(cnt_p + strlen(cnt_delimiter), &end, 10); + spi_throughput_data->test_packet_size = simple_strtoul(size_p + strlen(size_delimiter), &end, 10); + if(spi_throughput_data->test_packet_size > PAGE_SIZE) { + bes2600_info(BES2600_DBG_SPI, "%s, correct test_size %ld-->%ld\n", __func__, spi_throughput_data->test_packet_size, + PAGE_SIZE); + spi_throughput_data->test_packet_size = PAGE_SIZE; + } + spi_throughput_data->thread = kthread_run(spi_throughput_test, spi_throughput_data, "spi_throughput"); + } + + if (strncmp(data, "stop", 4) == 0) { + if (spi_throughput_data->thread) + kthread_stop(spi_throughput_data->thread); + } + +exit: + return count; +} + +static const struct file_operations spi_throughput_ops = { + .owner = THIS_MODULE, + .read = spi_throughput_read, + .write = spi_throughput_write, + .open = spi_throughput_open, + .release = spi_throughput_close, +}; + +static int create_spi_throughput_entry(struct sbus_priv *self) +{ + if (!bes_wlan_dir) { + bes_wlan_dir = proc_mkdir_data("bes_wlan", 0666, NULL, NULL); + if (!bes_wlan_dir) + return -ENOMEM; + } + + spi_throughput_data = kzalloc(sizeof(struct spi_throughput_struct), GFP_KERNEL); + if (!spi_throughput_data) + return -ENOMEM; + + spi_throughput_entry = proc_create_data("spi_throughput", 0666, bes_wlan_dir, &spi_throughput_ops, NULL); + if (!spi_throughput_entry) + return -ENOMEM; + + spi_throughput_data->bus_data = self; + + return 0; +} + +static int destory_spi_throughput_entry(void) +{ + if (spi_throughput_entry) { + if (spi_throughput_data) + kfree(spi_throughput_data); + proc_remove(spi_throughput_entry); + } + if (bes_wlan_dir) { + proc_remove(bes_wlan_dir); + } + return 0; +} + +static irqreturn_t bes2600_spi_irq_handler_test(int irq, void *dev_id) +{ + struct spi_throughput_struct *test = dev_id; + + test->irq_enters ++; + if (atomic_inc_return(&test->rx) == 1) { + wake_up(&test->test_wq); + } + return IRQ_HANDLED; +} + +static int bes2600_spi_irq_subscribe_test(struct sbus_priv *self, void *priv) +{ + int ret, irq; + + bes2600_info(BES2600_DBG_SPI, "%s\n", __func__); + + if (!IS_ERR_VALUE((uintptr_t)self->gpio_irq)) + irq = self->gpio_irq; + else + irq = self->func->irq; + + ret = request_threaded_irq(irq, NULL, + bes2600_spi_irq_handler_test, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "bes2600_wlan_irq", priv); + + if (WARN_ON(ret < 0)) + goto exit; + + ret = enable_irq_wake(irq); + if (WARN_ON(ret)) + goto free_irq; + + return 0; + +free_irq: + free_irq(irq, self); + +exit: + return ret; +} + +static int bes2600_spi_irq_unsubscribe_test(struct sbus_priv *self, void *priv) +{ + int ret = 0, irq; + + bes2600_info(BES2600_DBG_SPI, "%s\n", __func__); + + if (!IS_ERR_VALUE((uintptr_t)self->gpio_irq)) + irq = self->gpio_irq; + else + irq = self->func->irq; + + free_irq(irq, priv); + + return ret; +} + +static int spi_throughput_test(void *arg) +{ + u8 *test_data; + int ret, rx; + long status; + u32 next_len; + u32 val; + struct timeval time_before, time_after; + unsigned long jiffies_start = jiffies; + unsigned long interval; + struct spi_throughput_struct *test = spi_throughput_data; + struct sbus_priv *sbus_priv = test->bus_data; + bes2600_info(BES2600_DBG_SPI, "%s packets=%ld, packet_size=%ld\n", __func__, test->test_count, test->test_packet_size); + + test_data = (u8 *)__get_free_page(GFP_KERNEL); + if (!test_data) { + bes2600_err(BES2600_DBG_SPI, "%s alloc page failed\n", __func__); + goto exit; + } + + init_waitqueue_head(&test->test_wq); + bes2600_spi_sbus_ops.init(sbus_priv, NULL); + test->irq_enters = 0; + atomic_set(&test->rx, 0); + test->max_time = 0; + test->min_time = 100 * 1000000; + test->average_time = 0; + test->total_time = 0; + + allow_signal(SIGKILL | SIGINT | SIGTERM); + + bes2600_spi_irq_subscribe_test(sbus_priv, test); + + bes2600_spi_sbus_ops.lock(sbus_priv); + val = test->test_count; + bes2600_spi_sbus_ops.sbus_reg_write(sbus_priv, BES_LMAC_BUF_USE, &val, 4); + val = BES_HOST_SLAVE_SYNC; + bes2600_spi_sbus_ops.sbus_reg_write(sbus_priv, BES_HOST_SYNC_REG_ID, &val, 4); + bes2600_spi_sbus_ops.unlock(sbus_priv); + + do { + do_gettimeofday(&time_before); + status = wait_event_interruptible_timeout(test->test_wq, + ({rx = atomic_xchg(&test->rx, 0); (rx);}), MAX_SCHEDULE_TIMEOUT); + + if (status < 0 && status != -ERESTARTSYS) { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%ld\n", __func__, __LINE__, status); + break; + } + + bes2600_spi_sbus_ops.lock(sbus_priv); + ret = bes2600_spi_sbus_ops.sbus_reg_read(sbus_priv, BES_TX_CTRL_REG_ID, &next_len, 4); + bes2600_spi_sbus_ops.unlock(sbus_priv); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret); + break; + } + +rx_again: + if (next_len) { + bes2600_spi_sbus_ops.lock(sbus_priv); + ret = bes2600_spi_sbus_ops.sbus_memcpy_fromio(sbus_priv, + BES_TX_DATA_ADDR, test_data, next_len); + bes2600_spi_sbus_ops.unlock(sbus_priv); + if (ret) + break; + do_gettimeofday(&time_after); + interval = (time_after.tv_sec * 1000000 + time_after.tv_usec) - + (time_before.tv_sec * 1000000 + time_before.tv_usec); + if (test->max_time < interval) + test->max_time = interval; + if (test->min_time > interval) + test->min_time = interval; + if (test->average_time) { + test->average_time += interval; + test->average_time /= 2; + } else { + test->average_time = interval; + } + test->read_packets ++; + if (test->read_packets >= test->test_count) { + test->total_time = jiffies - jiffies_start; + bes2600_info(BES2600_DBG_SPI, "%s packets/irq=%ld %ld\n", __func__, test->read_packets, test->irq_enters); + break; + } + } + + if (next_len & 0xffff) { + next_len &= 0xffff0000; + goto rx_again; + } + + } while (!kthread_should_stop()); + free_page((unsigned long)test_data); + bes2600_spi_irq_unsubscribe_test(sbus_priv, test); +exit: + test->thread = NULL; + bes2600_info(BES2600_DBG_SPI, "%s exit ...\n", __func__); + bes2600_info(BES2600_DBG_SPI, "test results:%ld %ld %ld %ld\n", test->total_time, test->average_time, + test->max_time, test->min_time); + return 0; +} + +#endif + +#if defined(PLAT_ALLWINNER) || defined(PLAT_QCOM_QM215) +static struct bes2600_platform_data_spi bes_spi_plat_data = { + .spi_bits_per_word = 8, +}; +#endif + +struct bes2600_platform_data_spi *bes2600_get_platform_data(void) +{ +#if defined(PLAT_ALLWINNER) || defined(PLAT_QCOM_QM215) + return &bes_spi_plat_data; +#else + return NULL; +#endif +} + +static int bes2600_platform_data_init(struct device *dev) +{ + int ret = 0; + struct device_node *np = dev->of_node; + struct gpio_config config; + struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data(); + + if (!pdata) + return -ENODEV; + + pdata->spi_bits_per_word = 8; + pdata->priv = to_spi_device(dev); + + pdata->reset = of_get_named_gpio_flags(np, "reset", + 0, (enum of_gpio_flags *)&config); + /* Ensure I/Os are pulled low */ + if (gpio_is_valid(pdata->reset)) { + ret = gpio_request(pdata->reset, "bes2600_wlan_reset"); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "can't request reset_gpio:%d\n", ret); + pdata->reset = -1; + goto exit; + } else { + gpio_direction_output(pdata->reset, 0); + } + } else { + bes2600_err(BES2600_DBG_SPI, "reset is invalid\n"); + } + + pdata->powerup = of_get_named_gpio_flags(np, "powerup", + 0, (enum of_gpio_flags *)&config); + if (gpio_is_valid(pdata->powerup)) { + ret = gpio_request(pdata->powerup, "bes2600_wlan_powerup"); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "can't request powerup_gpio:%d\n", ret); + pdata->powerup = -1; + goto exit; + } else { + gpio_direction_output(pdata->powerup, 0); + } + } else { + bes2600_err(BES2600_DBG_SPI, "powerup is invalid\n"); + } + + pdata->irq_gpio = of_get_named_gpio_flags(np, "irq_gpio", 0, (enum of_gpio_flags *)&config); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request(pdata->irq_gpio, "bes2600_wlan_irq_gpio"); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "can't request irq gpio:%d\n", ret); + goto exit; + } else { + ret = gpio_direction_input(pdata->irq_gpio); + if (ret < 0) { + bes2600_err(BES2600_DBG_SPI, "can't config irq_gpio input\n"); + pdata->irq_gpio = -1; + goto exit; + } + } + } else { + bes2600_err(BES2600_DBG_SPI, "irq gpio is invalid\n"); + } + + pdata->host_wakeup_wlan = of_get_named_gpio_flags(np, "host_wakeup_wlan", 0, (enum of_gpio_flags *)&config); + if (gpio_is_valid(pdata->host_wakeup_wlan)) { + ret = gpio_request(pdata->host_wakeup_wlan, "bes2600_host_wakeup_wlan"); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "can't request host_wakeup_wlan gpio:%d\n", ret); + goto exit; + } else { + gpio_direction_output(pdata->host_wakeup_wlan, 0); + } + } else { + bes2600_err(BES2600_DBG_SPI, "irq gpio is invalid\n"); + } + +exit: + return ret; +} + +static void bes2600_platform_data_deinit(void) +{ + const struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data(); + + if (pdata == NULL) { + return; + } + + if (gpio_is_valid(pdata->reset)) { + gpio_free(pdata->reset); + } + + if (gpio_is_valid(pdata->powerup)) { + gpio_free(pdata->powerup); + } + + if (gpio_is_valid(pdata->irq_gpio)) { + gpio_free(pdata->irq_gpio); + } + + if (gpio_is_valid(pdata->host_wakeup_wlan)) { + gpio_free(pdata->host_wakeup_wlan); + } + +} + +static int bes2600_spi_reset(struct sbus_priv *self) +{ + struct bes2600_platform_data_spi *plat_data = bes2600_get_platform_data(); + + bes2600_info(BES2600_DBG_SPI, "%s ...\n", __func__); + + if (plat_data == NULL) + return 0; + + if (gpio_is_valid(plat_data->reset)) { + gpio_set_value(plat_data->reset, 1); + mdelay(50); + gpio_set_value(plat_data->reset, 0); + } + return 0; +} + +static int bes2600_spi_awake(struct sbus_priv *self) +{ + int retries = 0; + u32 sync_header; + + bes2600_spi_lock(self); + +#if 0 + if (gpio_is_valid(pdata->host_wakeup_wlan)) { + bes2600_dbg(BES2600_DBG_SPI, "toggle wake up line\n"); + gpio_set_value(pdata->host_wakeup_wlan, 1); + msleep(20); + gpio_set_value(pdata->host_wakeup_wlan, 0); + } else { + bes2600_err(BES2600_DBG_SPI, "toggle wake up line failed\n"); + } +#endif + + do { + bes2600_spi_reg_read(self, BES_HOST_SYNC_REG_ID, &sync_header, 4); + if (sync_header == BES_SLAVE_SYNC_HEADER) { + self->spi_active = true; + break; + } else { + msleep(10); + } + ++retries; + } while (retries < 5); + bes2600_spi_unlock(self); + + if (retries >= 5) { + bes2600_err(BES2600_DBG_SPI, "%s spi sync failed(%x)\n", __func__, sync_header); + return -1; + } + + return 0; +} + +static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int falg) +{ + unsigned long flags; + bool gpio_wakeup = false; + const struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data(); + if (pdata == NULL) + return; + + bes2600_info(BES2600_DBG_SPI, "%s with %d\n", __func__, falg); + + /* error check */ + if(test_bit(falg, &self->gpio_wakup_flags)) { + bes2600_err(BES2600_DBG_SPI, + "repeat set gpio_wake_flag, sub_sys:%d", falg); + return; + } + + /* check if this is the first subsystem that need mcu to keep awake */ + local_irq_save(flags); + gpio_wakeup = (self->gpio_wakup_flags == 0); + local_irq_restore(flags); + + /* do wakeup mcu operation */ + if(gpio_wakeup) { + bes2600_info(BES2600_DBG_SPI, "pull high gpio by flag:%d\n", falg); + if (gpio_is_valid(pdata->host_wakeup_wlan)) { + gpio_set_value(pdata->host_wakeup_wlan, 1); + msleep(1); + } else { + bes2600_err(BES2600_DBG_SPI, + "%s, wakeup gpio is invalid\n", __func__); + } + } + + /* set flag of gpio_wakeup_flags */ + set_bit(falg, &self->gpio_wakup_flags); +} + +static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int falg) +{ + unsigned long flags; + bool gpio_sleep = false; + const struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data(); + if (pdata == NULL) + return; + + bes2600_info(BES2600_DBG_SPI, "%s with %d\n", __func__, falg); + + /* error check */ + if(test_bit(falg, &self->gpio_wakup_flags) == 0) { + bes2600_err(BES2600_DBG_SPI, + "repeat clear gpio_wake_flag, sub_sys:%d", falg); + return; + } + + + /* clear flag of gpio_wakeup_flags */ + clear_bit(falg, &self->gpio_wakup_flags); + + /* check if this is the last subsystem that need mcu to keep awake */ + local_irq_save(flags); + gpio_sleep = (self->gpio_wakup_flags == 0); + local_irq_restore(flags); + + /* do wakeup mcu operation */ + if(gpio_sleep) { + bes2600_info(BES2600_DBG_SPI, "pull low gpio by flag:%d\n", falg); + if (gpio_is_valid(pdata->host_wakeup_wlan)) { + gpio_set_value(pdata->host_wakeup_wlan, 0); + } else { + bes2600_err(BES2600_DBG_SPI, + "%s, wakeup gpio is invalid\n", __func__); + } + } +} + +int bes2600_spi_active(struct sbus_priv *self, int sub_system) +{ + int ret = 0; + + if (bes2600_chrdev_is_signal_mode() || sub_system == SUBSYSTEM_WIFI) { + uint32_t subint = 0, int_type = BES_HOST_INT_SUBINT, status, cfm; + int retries; + + if (sub_system == SUBSYSTEM_MCU) { + subint |= BES_SUBSYSTEM_MCU_ACTIVE; + cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY; + } else if (sub_system == SUBSYSTEM_WIFI) { + subint |= BES_SUBSYSTEM_WIFI_ACTIVE; + cfm = BES_SLAVE_STATUS_WIFI_OPEN_READY; + } else if (sub_system == SUBSYSTEM_BT) { + subint |= BES_SUBSYSTEM_BT_ACTIVE; + cfm = BES_SLAVE_STATUS_BT_OPEN_READY; + } else { + return -EINVAL; + } + + if ((ret = bes2600_spi_awake(self))) + return ret; + + bes2600_spi_lock(self); + + retries = 0; + do { + if ((ret = bes2600_spi_reg_write(self, BES_HOST_SUBINT_REG_ID, &subint, 4))) + goto exit; + if ((ret = bes2600_spi_reg_write(self, BES_HOST_INT_REG_ID, &int_type, 4))) + goto exit; + msleep(25); + if ((ret = bes2600_spi_reg_read(self, BES_SLAVE_STATUS_REG_ID, &status, 4))) + goto exit; + if (status & cfm) + break; + retries++; + } while (retries < 80); + + if (retries >= 80) { + bes2600_err(BES2600_DBG_SPI, "bes2600_spi_active(%d) failed(0x%x)\n", sub_system, status); + ret = -110; + goto exit; + } +exit: + bes2600_spi_unlock(self); + } + + return ret; +} + +static void bes2600_spi_work_empty(struct sbus_priv *self) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&self->rx_queue))) { + dev_kfree_skb(skb); + } + + self->lmac_ptr_top = 0; + self->lmac_ptr = 0; + self->lmac_addr_min = 0; + self->lmac_addr_max = 0; + self->packets_upload = 0; +} + +int bes2600_spi_deactive(struct sbus_priv *self, int sub_system) +{ + int ret; + + if (bes2600_chrdev_is_signal_mode()) { + u32 subint, cfm, status, int_type = BES_HOST_INT_SUBINT, sync = 0; + int retries; + + if (sub_system == SUBSYSTEM_MCU) { + subint = BES_SUBSYSTEM_MCU_DEACTIVE; + cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY; + } else if (sub_system == SUBSYSTEM_WIFI) { + subint = BES_SUBSYSTEM_WIFI_DEACTIVE; + cfm = BES_SLAVE_STATUS_WIFI_OPEN_READY; + } else if (sub_system == SUBSYSTEM_BT) { + subint = BES_SUBSYSTEM_BT_DEACTIVE; + cfm = BES_SLAVE_STATUS_BT_OPEN_READY; + } else { + return -EINVAL; + } + + bes2600_spi_lock(self); + + retries = 0; + do { + if ((ret = bes2600_spi_reg_write(self, BES_HOST_SUBINT_REG_ID, &subint, 4))) + goto exit; + if ((ret = bes2600_spi_reg_write(self, BES_HOST_INT_REG_ID, &int_type, 4))) + goto exit; + msleep(10); + bes2600_spi_reg_read(self, BES_HOST_SYNC_REG_ID, &sync, 4); + if ((ret = bes2600_spi_reg_read(self, BES_SLAVE_STATUS_REG_ID, &status, 4))) + goto exit; + if ( (sync != BES_SLAVE_SYNC_HEADER) || !(cfm & status) ) { + break; + } + retries++; + } while (retries < 5); + + if (retries >= 5) { + ret = -EIO; + bes2600_err(BES2600_DBG_SPI, "bes2600 spi deactive(%d) failed\n", sub_system); + goto exit; + } + + if (sub_system == SUBSYSTEM_MCU) + self->spi_active = false; + + if (sub_system == SUBSYSTEM_WIFI) + bes2600_spi_work_empty(self); + + } else { + return 0; + } + +exit: + bes2600_spi_unlock(self); + return ret; +} + +static int bes2600_spi_power_switch (struct sbus_priv *self, int on); +static struct sbus_ops bes2600_spi_sbus_ops = { + .init = bes2600_spi_init, + .sbus_memcpy_fromio = bes2600_spi_memcpy_fromio, + .sbus_memcpy_toio = bes2600_spi_memcpy_toio, + .lock = bes2600_spi_lock, + .unlock = bes2600_spi_unlock, + .irq_subscribe = bes2600_spi_irq_subscribe, + .irq_unsubscribe = bes2600_spi_irq_unsubscribe, + .align_size = bes2600_spi_align_size, + .set_block_size = bes2600_spi_set_block_size, + .sbus_reg_read = bes2600_spi_reg_read, + .sbus_reg_write = bes2600_spi_reg_write, + .pipe_read = bes2600_spi_pipe_read, + .reset = bes2600_spi_reset, + .sbus_active = bes2600_spi_active, + .sbus_deactive = bes2600_spi_deactive, + .power_switch = bes2600_spi_power_switch, + .gpio_wake = bes2600_gpio_wakeup_mcu, + .gpio_sleep = bes2600_gpio_allow_mcu_sleep, +}; + +/* Probe Function to be called by SPI stack when device is discovered */ +static int bes2600_spi_probe(struct spi_device *func) +{ + int ret; + const struct bes2600_platform_data_spi *plat_data; + struct sbus_priv *self; + + if ((ret = bes2600_platform_data_init(&func->dev))) + return ret; + + plat_data = bes2600_get_platform_data(); + if ((ret = bes2600_spi_on(plat_data))) + goto err_on; + + /* Sanity check speed */ +#ifdef PLAT_ALLWINNER_T507 + if (!func->master->max_speed_hz || func->master->max_speed_hz > 48000000) + func->max_speed_hz = 48000000; + else + func->max_speed_hz = func->master->max_speed_hz; +#endif + + /* Fix up transfer size */ + if (plat_data && plat_data->spi_bits_per_word) + func->bits_per_word = plat_data->spi_bits_per_word; + if (!func->bits_per_word) + func->bits_per_word = 16; + + /* And finally.. */ + func->mode = SPI_MODE_3; + + bes2600_info(BES2600_DBG_SPI, "bes2600_wlan_spi: Probe called (FUNC %p CS %d M %d BPW %d CLK %d).\n", + func, func->chip_select, func->mode, func->bits_per_word, + func->max_speed_hz); + + if ((ret = spi_setup(func))) { + bes2600_err(BES2600_DBG_SPI, "spi_setup() failed(%d)!\n", ret); + goto err_setup; + } + + self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL); + if (!self) { + bes2600_err(BES2600_DBG_SPI, "Can't allocate SPI sbus_priv."); + ret = -ENOMEM; + goto err_nomem; + } + pr_info("%s self=%p\n", __func__, self); + + self->pdata = plat_data; + self->func = func; + self->dev = &func->dev; + self->gpio_wakup_flags = 0; + if (bes2600_chrdev_is_signal_mode()) + self->spi_active = false; + else + self->spi_active = true; + + spin_lock_init(&self->lock); + + spi_set_drvdata(func, self); + + init_waitqueue_head(&self->wq); + + bes2600_reg_set_object(&bes2600_spi_sbus_ops, self); + + if ((ret = bes2600_load_firmware(&bes2600_spi_sbus_ops, self)) < 0) { + bes2600_err(BES2600_DBG_SPI, "bes2600_load_firmware failed(%d)\n", ret); + goto err_probe; + } + + if (ret) { + bes2600_chrdev_set_sbus_priv_data(NULL, true); + bes2600_spi_off(plat_data); + goto out; + } + +#ifdef CONFIG_BES2600_SPI_THROUGHPUT_TEST + return create_spi_throughput_entry(self); +#endif + if ((ret = bes2600_core_probe(&bes2600_spi_sbus_ops, + self, &func->dev, &self->core))) { + bes2600_err(BES2600_DBG_SPI, "bes2600_core_probe failed(%d)\n", ret); + goto err_probe; + } + bes2600_chrdev_set_sbus_priv_data(self, false); + +out: + return 0; + +err_probe: + bes2600_reg_set_object(NULL, NULL); +err_nomem: +err_setup: + bes2600_spi_off(plat_data); +err_on: + bes2600_platform_data_deinit(); + return ret; +} + +int bes2600_register_net_dev(struct sbus_priv *bus_priv) +{ + int status = 0; + BUG_ON(!bus_priv); + status = bes2600_core_probe(&bes2600_spi_sbus_ops, + bus_priv, bus_priv->dev, &bus_priv->core); + + return status; +} + +int bes2600_unregister_net_dev(struct sbus_priv *bus_priv) +{ + BUG_ON(!bus_priv); + if (bus_priv->core) { + bes2600_core_release(bus_priv->core); + bus_priv->core = NULL; + } + return 0; +} + +bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv) +{ + BUG_ON(!bus_priv); + return (bus_priv->core != NULL); +} + +static int bes2600_spi_power_switch (struct sbus_priv *null_self, int on) +{ + int ret; + struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data(); + struct sbus_priv *self = spi_get_drvdata((struct spi_device *)pdata->priv); + pr_info("self:%p pdata:%p\n", self, pdata); + if (on) { + ret = bes2600_spi_on(pdata); + if ((ret = bes2600_load_firmware(&bes2600_spi_sbus_ops, self)) < 0) { + bes2600_err(BES2600_DBG_SPI, "%s bes2600_load_firmware failed(%d)\n", __func__, ret); + goto off; + } + if ((ret = bes2600_register_net_dev(self))) + goto off; + bes2600_chrdev_set_sbus_priv_data(self, false); + return 0; + } else { + bes2600_unregister_net_dev(self); + bes2600_chrdev_set_sbus_priv_data(NULL, false); + } + +off: + ret = bes2600_spi_off(self->pdata); + return ret; +} + +/* Disconnect Function to be called by SPI stack when device is disconnected */ +static int bes2600_spi_disconnect(struct spi_device *func) +{ + struct sbus_priv *self = spi_get_drvdata(func); + + bes2600_info(BES2600_DBG_SPI, "%s\n", __func__); + +#ifdef CONFIG_BES2600_SPI_THROUGHPUT_TEST + destory_spi_throughput_entry(); +#endif + + if (self) { + //bes2600_spi_irq_unsubscribe(self); + if (self->core) { + bes2600_core_release(self->core); + bes2600_spi_off(self->pdata); + if (self->tx_dummy_buf) + free_pages((unsigned long)self->tx_dummy_buf, get_order((1632 + 24)) * MAX_SEND_PACKETS_NUM); + self->core = NULL; + } + } + + bes2600_reg_set_object(NULL, NULL); + bes2600_platform_data_deinit(); + + return 0; +} + +static int __maybe_unused bes2600_spi_suspend(struct device *dev) +{ + int ret = 0; + struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev)); + + bes2600_info(BES2600_DBG_SPI, "%s enter\n", __func__); + + bes2600_spi_lock(self); + if (self->spi_active == true) + ret = -EBUSY; + bes2600_spi_unlock(self); + + /* XXX notify host that we have to keep BES2600 powered on? */ + return ret; +} + +static int __maybe_unused bes2600_spi_resume(struct device *dev) +{ + /* SPI master shoud be reinitialize? */ + return 0; +} + +static SIMPLE_DEV_PM_OPS(bes2600_pm_ops, bes2600_spi_suspend, bes2600_spi_resume); + +static const struct of_device_id bes_spi_dev_ids[] = { + {.compatible = "bes,wlan_2002"}, + {}, +}; + +static struct spi_driver spi_driver = { + .probe = bes2600_spi_probe, + .remove = bes2600_spi_disconnect, + .driver = { + .name = "bes2600_wlan_spi", + .pm = IS_ENABLED(CONFIG_PM) ? &bes2600_pm_ops : NULL, + .of_match_table = of_match_ptr(bes_spi_dev_ids), + }, +}; + + +/* Init Module function -> Called by insmod */ +static int __init bes2600_spi_module_init(void) +{ + int ret; + + bes2600_info(BES2600_DBG_SPI, "------Driver: bes2600.ko version :%s\n", BES2600_DRV_VERSION); + + bes2600_chrdev_update_signal_mode(); + + ret = bes2600_chrdev_init(&bes2600_spi_sbus_ops); + if(ret) + return ret; + + ret = spi_register_driver(&spi_driver); + + return ret; +} + +/* Called at Driver Unloading */ +static void __exit bes2600_spi_module_exit(void) +{ + spi_unregister_driver(&spi_driver); + bes2600_chrdev_free(); +} + +module_init(bes2600_spi_module_init); +module_exit(bes2600_spi_module_exit); diff --git a/drivers/staging/bes2600/bes2600_usb.c b/drivers/staging/bes2600/bes2600_usb.c new file mode 100644 index 000000000000..e56750c13fd6 --- /dev/null +++ b/drivers/staging/bes2600/bes2600_usb.c @@ -0,0 +1,1554 @@ +#include +#include +#include "bes2600.h" +#include "sbus.h" +#include "wsm.h" +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" +#define BES2600_USB_PIPE_INVALID BES2600_USB_PIPE_MAX + +struct sbus_priv; +/* usb device object */ +struct bes2600_usb_pipe { + struct list_head urb_list_head; + struct usb_anchor urb_submitted; + u32 urb_alloc; + u32 urb_cnt; + u32 urb_cnt_thresh; + atomic_t urb_pending_cnt; + unsigned int usb_pipe_handle; + u32 flags; + u8 ep_address; + u8 logical_pipe_num; + struct sbus_priv *ar_usb; + u16 max_packet_size; + struct work_struct io_complete_work; + struct sk_buff_head io_comp_queue; + struct usb_endpoint_descriptor *ep_desc; +}; + +struct sbus_priv { + /* protects pipe->urb_list_head and pipe->urb_cnt */ + spinlock_t cs_lock; + + struct usb_device *udev; + struct usb_interface *interface; + struct bes2600_usb_pipe pipes[BES2600_USB_PIPE_MAX]; + u8 *diag_cmd_buffer; + u8 *diag_resp_buffer; + struct bes2600_common *ar; + spinlock_t rx_queue_lock; + struct sk_buff_head rx_queue; + void * btdev; + + spinlock_t status_lock; + sbus_irq_handler usb_irq_handler; + void *irq_data; + u32 int_control_reg; + u32 int_status_reg; +}; + +#define BES2600_USB_PIPE_FLAG_TX (1 << 0) + +/* usb urb object */ +struct bes2600_urb_context { + struct list_head link; + struct bes2600_usb_pipe *pipe; + struct sk_buff *skb; + struct urb *urb; + struct bes2600_common *ar; +}; + +/* constants */ +#define TX_URB_COUNT 49 +#define RX_URB_COUNT 32 +#define BES2600_USB_RX_BUFFER_SIZE 8192 + + +/* USB endpoint definitions */ +#define BES2600_USB_EP_ADDR_CTRL_IN 0x81 +#define BES2600_USB_EP_ADDR_WLAN_IN 0x82 +#define BES2600_USB_EP_ADDR_BT_IN 0x83 + +#define BES2600_USB_EP_ADDR_CTRL_OUT 0x01 +#define BES2600_USB_EP_ADDR_WLAN_OUT 0x02 +#define BES2600_USB_EP_ADDR_BT_OUT 0x03 + +/* diagnostic command defnitions */ +#define BES2600_USB_CONTROL_REQ_SEND_BMI_CMD 1 +#define BES2600_USB_CONTROL_REQ_RECV_BMI_RESP 2 +#define BES2600_USB_CONTROL_REQ_DIAG_CMD 3 +#define BES2600_USB_CONTROL_REQ_DIAG_RESP 4 + +#define BES2600_USB_CTRL_DIAG_CC_READ 0 +#define BES2600_USB_CTRL_DIAG_CC_WRITE 1 + +struct bes2600_usb_ctrl_diag_cmd_write { + __le32 cmd; + __le32 address; + __le32 value; + __le32 _pad[1]; +} __packed; + +struct bes2600_usb_ctrl_diag_cmd_read { + __le32 cmd; + __le32 address; +} __packed; + +struct bes2600_usb_ctrl_diag_resp_read { + __le32 value; +} __packed; + +/* function declarations */ +static void bes2600_usb_recv_complete(struct urb *urb); + +#define BES2600_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02) +#define BES2600_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03) +#define BES2600_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01) +#define BES2600_USB_IS_DIR_IN(addr) ((addr) & 0x80) + +static void bes2600_usb_irq_handler(struct sbus_priv *ar_usb) +{ + sbus_irq_handler handler = NULL; + void *priv_data = NULL; + unsigned long flags; + + spin_lock_irqsave(&ar_usb->status_lock,flags); + handler = ar_usb->usb_irq_handler; + priv_data = ar_usb->irq_data; + spin_unlock_irqrestore(&ar_usb->status_lock,flags); + + if(handler != 0) + handler(priv_data); +} + +static int bes2600_usb_test_control_reg(struct sbus_priv *ar_usb, int bitval) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&ar_usb->status_lock,flags); + ret = ar_usb->int_control_reg & bitval; + spin_unlock_irqrestore(&ar_usb->status_lock,flags); + return ret; +} + +static void bes2600_usb_set_status_reg(struct sbus_priv *ar_usb, int bitval) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&ar_usb->status_lock,flags); + ret = ar_usb->int_status_reg |= bitval; + spin_unlock_irqrestore(&ar_usb->status_lock,flags); +} + +/* pipe/urb operations */ +static struct bes2600_urb_context * +bes2600_usb_alloc_urb_from_pipe(struct bes2600_usb_pipe *pipe) +{ + struct bes2600_urb_context *urb_context = NULL; + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + if (!list_empty(&pipe->urb_list_head)) { + urb_context = + list_first_entry(&pipe->urb_list_head, + struct bes2600_urb_context, link); + list_del(&urb_context->link); + pipe->urb_cnt--; + } + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); + + return urb_context; +} + +static void bes2600_usb_free_urb_to_pipe(struct bes2600_usb_pipe *pipe, + struct bes2600_urb_context *urb_context) +{ + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + pipe->urb_cnt++; + + list_add(&urb_context->link, &pipe->urb_list_head); + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); +} + +static void bes2600_usb_cleanup_recv_urb(struct bes2600_urb_context *urb_context) +{ + if (urb_context->skb) { + dev_kfree_skb(urb_context->skb); + urb_context->skb = NULL; + } + bes2600_usb_free_urb_to_pipe(urb_context->pipe, urb_context); +} + +static inline struct sbus_priv *bes2600_usb_priv(struct bes2600_common *ar) +{ + return (struct sbus_priv *)ar->sbus_priv; +} + +/* pipe resource allocation/cleanup */ +static int bes2600_usb_alloc_pipe_resources(struct bes2600_usb_pipe *pipe, + int urb_cnt) +{ + struct bes2600_urb_context *urb_context; + int status = 0, i; + + INIT_LIST_HEAD(&pipe->urb_list_head); + init_usb_anchor(&pipe->urb_submitted); + + for (i = 0; i < urb_cnt; i++) { + urb_context = kzalloc(sizeof(struct bes2600_urb_context), + GFP_KERNEL); + if (urb_context == NULL) { + status = -ENOMEM; + goto fail_alloc_pipe_resources; + } + + urb_context->urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb_context->urb == NULL) { + kfree(urb_context); + status = -ENOMEM; + goto fail_alloc_pipe_resources; + } + urb_context->pipe = pipe; + + /* + * we are only allocate the urb contexts here, the actual URB + * is allocated from the kernel as needed to do a transaction + */ + pipe->urb_alloc++; + bes2600_usb_free_urb_to_pipe(pipe, urb_context); + } + + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb: alloc resources lpipe:%d hpipe:0x%X urbs:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc); + +fail_alloc_pipe_resources: + return status; +} + +static void bes2600_usb_free_pipe_resources(struct bes2600_usb_pipe *pipe) +{ + struct bes2600_urb_context *urb_context; + + if (pipe->ar_usb == NULL) { + /* nothing allocated for this pipe */ + return; + } + + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb: free resources lpipe:%d" + "hpipe:0x%X urbs:%d avail:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + + if (pipe->urb_alloc != pipe->urb_cnt) { + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb: urb leak! lpipe:%d" + "hpipe:0x%X urbs:%d avail:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + } + + while (true) { + urb_context = bes2600_usb_alloc_urb_from_pipe(pipe); + if (urb_context == NULL) + break; + usb_free_urb(urb_context->urb); + urb_context->urb = NULL; + kfree(urb_context); + } +} + +static void bes2600_usb_cleanup_pipe_resources(struct sbus_priv *ar_usb) +{ + int i; + + for (i = 0; i < BES2600_USB_PIPE_MAX; i++){ + if((i != BES2600_USB_PIPE_RX_BT) && + (i != BES2600_USB_PIPE_TX_BT)) + bes2600_usb_free_pipe_resources(&ar_usb->pipes[i]); + } +} + +static u8 bes2600_usb_get_logical_pipe_num(struct sbus_priv *ar_usb, + u8 ep_address, int *urb_count) +{ + u8 pipe_num = BES2600_USB_PIPE_INVALID; + + switch (ep_address) { + case BES2600_USB_EP_ADDR_CTRL_IN: + pipe_num = BES2600_USB_PIPE_RX_CTRL; + *urb_count = RX_URB_COUNT; + break; + case BES2600_USB_EP_ADDR_WLAN_IN: + pipe_num = BES2600_USB_PIPE_RX_WLAN; + *urb_count = RX_URB_COUNT; + break; + case BES2600_USB_EP_ADDR_BT_IN: + pipe_num = BES2600_USB_PIPE_RX_BT; + *urb_count = RX_URB_COUNT; + break; + case BES2600_USB_EP_ADDR_CTRL_OUT: + pipe_num = BES2600_USB_PIPE_TX_CTRL; + *urb_count = TX_URB_COUNT; + break; + case BES2600_USB_EP_ADDR_WLAN_OUT: + pipe_num = BES2600_USB_PIPE_TX_WLAN; + *urb_count = TX_URB_COUNT; + break; + case BES2600_USB_EP_ADDR_BT_OUT: + pipe_num = BES2600_USB_PIPE_TX_BT; + *urb_count = TX_URB_COUNT; + break; + default: + /* note: there may be endpoints not currently used */ + break; + } + + return pipe_num; +} + +static int bes2600_usb_setup_pipe_resources(struct sbus_priv *ar_usb) +{ + struct usb_interface *interface = ar_usb->interface; + struct usb_host_interface *iface_desc = interface->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct bes2600_usb_pipe *pipe; + int i, urbcount, status = 0; + u8 pipe_num; + + bes2600_dbg(BES2600_DBG_USB, "setting up USB Pipes using interface\n"); + + /* walk decriptors and setup pipes */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (BES2600_USB_IS_BULK_EP(endpoint->bmAttributes)) { + bes2600_dbg(BES2600_DBG_USB, + "%s Bulk Ep:0x%2.2X maxpktsz:%d\n", + BES2600_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize)); + } else if (BES2600_USB_IS_INT_EP(endpoint->bmAttributes)) { + bes2600_dbg(BES2600_DBG_USB, + "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n", + BES2600_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } else if (BES2600_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + bes2600_dbg(BES2600_DBG_USB, + "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n", + BES2600_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } + urbcount = 0; + + pipe_num = + bes2600_usb_get_logical_pipe_num(ar_usb, + endpoint->bEndpointAddress, + &urbcount); + if (pipe_num == BES2600_USB_PIPE_INVALID) + continue; + + pipe = &ar_usb->pipes[pipe_num]; + if (pipe->ar_usb != NULL) { + /* hmmm..pipe was already setup */ + continue; + } + + atomic_set(&pipe->urb_pending_cnt, 0); + pipe->ar_usb = ar_usb; + pipe->logical_pipe_num = pipe_num; + pipe->ep_address = endpoint->bEndpointAddress; + pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); + + if (BES2600_USB_IS_BULK_EP(endpoint->bmAttributes)) { + if (BES2600_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvbulkpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndbulkpipe(ar_usb->udev, + pipe->ep_address); + } + } else if (BES2600_USB_IS_INT_EP(endpoint->bmAttributes)) { + if (BES2600_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvintpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndintpipe(ar_usb->udev, + pipe->ep_address); + } + } else if (BES2600_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + if (BES2600_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvisocpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndisocpipe(ar_usb->udev, + pipe->ep_address); + } + } + + pipe->ep_desc = endpoint; + + if (!BES2600_USB_IS_DIR_IN(pipe->ep_address)) + pipe->flags |= BES2600_USB_PIPE_FLAG_TX; + + if((pipe_num != BES2600_USB_PIPE_RX_BT) && + (pipe_num != BES2600_USB_PIPE_TX_BT)){ + status = bes2600_usb_alloc_pipe_resources(pipe, urbcount); + if (status != 0) + break; + } + } +#ifdef CONFIG_BES2600_BT + status = bes2600_btusb_setup_pipes(ar_usb); +#endif + + return status; +} + +/* pipe operations */ +static void bes2600_usb_post_recv_transfers(struct bes2600_usb_pipe *recv_pipe, + int buffer_length) +{ + struct bes2600_urb_context *urb_context; + struct urb *urb; + int usb_status; + + while (true) { + urb_context = bes2600_usb_alloc_urb_from_pipe(recv_pipe); + if (urb_context == NULL) + break; + + urb_context->skb = dev_alloc_skb(buffer_length); + if (urb_context->skb == NULL) + goto err_cleanup_urb; + + urb = urb_context->urb; + BUG_ON(!urb); + usb_fill_bulk_urb(urb, + recv_pipe->ar_usb->udev, + recv_pipe->usb_pipe_handle, + urb_context->skb->data, + buffer_length, + bes2600_usb_recv_complete, urb_context); + + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n", + recv_pipe->logical_pipe_num, + recv_pipe->usb_pipe_handle, recv_pipe->ep_address, + buffer_length, urb_context->skb); + + usb_anchor_urb(urb, &recv_pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + if (usb_status) { + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb : usb bulk recv failed %d\n", + usb_status); + usb_unanchor_urb(urb); + goto err_cleanup_urb; + } + } + return; + +err_cleanup_urb: + bes2600_usb_cleanup_recv_urb(urb_context); + return; +} + +static void bes2600_usb_flush_all(struct sbus_priv *ar_usb) +{ + int i; + + for (i = 0; i < BES2600_USB_PIPE_MAX; i++) { + if((i != BES2600_USB_PIPE_RX_BT) && + (i != BES2600_USB_PIPE_TX_BT)){ + if (ar_usb->pipes[i].ar_usb != NULL) + usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted); + } + } + + /* + * Flushing any pending I/O may schedule work this call will block + * until all scheduled work runs to completion. + */ + flush_scheduled_work(); +} + +static void bes2600_usb_start_recv_pipes(struct sbus_priv *ar_usb) +{ + ar_usb->pipes[BES2600_USB_PIPE_RX_WLAN].urb_cnt_thresh = 1; + bes2600_usb_post_recv_transfers(&ar_usb->pipes[BES2600_USB_PIPE_RX_WLAN], BES2600_USB_RX_BUFFER_SIZE); +} + +void bes2600_recv_buf_put(struct sbus_priv *ar_usb, struct sk_buff *skb) +{ + unsigned long flags; + if (!skb) + return; + + spin_lock_irqsave(&ar_usb->rx_queue_lock, flags); + skb_queue_tail(&ar_usb->rx_queue, skb); + spin_unlock_irqrestore(&ar_usb->rx_queue_lock, flags); +} + +struct sk_buff *bes2600_recv_buf_get(struct sbus_priv *ar_usb) +{ + unsigned long flags; + struct sk_buff *skb = NULL; + + spin_lock_irqsave(&ar_usb->rx_queue_lock, flags); + skb = skb_dequeue(&ar_usb->rx_queue); + spin_unlock_irqrestore(&ar_usb->rx_queue_lock, flags); + + return skb; +} +/* hif usb rx/tx completion functions */ +static void bes2600_usb_recv_complete(struct urb *urb) +{ + struct bes2600_urb_context *urb_context = urb->context; + struct bes2600_usb_pipe *pipe = urb_context->pipe; + struct sk_buff *skb = NULL; + int status = 0; + + bes2600_dbg(BES2600_DBG_USB, + "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__, + pipe->logical_pipe_num, urb->status, urb->actual_length, + urb); + + if (urb->status != 0) { + status = -EIO; + switch (urb->status) { + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* + * no need to spew these errors when device + * removed or urb killed due to driver shutdown + */ + status = -ECANCELED; + break; + default: + bes2600_dbg(BES2600_DBG_USB, + "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", + __func__, pipe->logical_pipe_num, + pipe->ep_address, urb->status); + break; + } + goto cleanup_recv_urb; + } + + if (urb->actual_length == 0) + goto cleanup_recv_urb; + + skb = urb_context->skb; + skb_put(skb, urb->actual_length); + + /* we are going to pass it up */ + urb_context->skb = NULL; + + // set virtual register + if(bes2600_usb_test_control_reg(pipe->ar_usb, BES_USB_FW_RX_INDICATION)) + bes2600_usb_set_status_reg(pipe->ar_usb, BES_USB_FW_RX_INDICATION); + + /* note: queue implements a lock */ +#ifndef BES2600_RX_IN_BH + skb_queue_tail(&pipe->io_comp_queue, skb); + schedule_work(&pipe->io_complete_work); +#else + bes2600_recv_buf_put(pipe->ar_usb, skb); + bes2600_usb_irq_handler(pipe->ar_usb); +#endif +cleanup_recv_urb: + bes2600_usb_cleanup_recv_urb(urb_context); + usb_unanchor_urb(urb); + + if (status == 0 && + pipe->urb_cnt >= pipe->urb_cnt_thresh) { + /* our free urbs are piling up, post more transfers */ + bes2600_usb_post_recv_transfers(pipe, BES2600_USB_RX_BUFFER_SIZE); + } +} + +static void bes2600_usb_transmit_complete(struct urb *urb) +{ + struct bes2600_urb_context *urb_context = urb->context; + struct bes2600_usb_pipe *pipe = urb_context->pipe; + struct sk_buff *skb; + + bes2600_dbg(BES2600_DBG_USB, "%s: pipe: %d, stat:%d, len:%d\n", + __func__, pipe->logical_pipe_num, urb->status, urb->actual_length); + + if (urb->status != 0) { + bes2600_err(BES2600_DBG_USB, + "%s: pipe: %d, failed:%d\n", + __func__, pipe->logical_pipe_num, urb->status); + } + + skb = urb_context->skb; + urb_context->skb = NULL; + bes2600_usb_free_urb_to_pipe(urb_context->pipe, urb_context); + usb_unanchor_urb(urb); + + atomic_sub(1, &pipe->urb_pending_cnt); + + // set virtual register + if(!urb->status && bes2600_usb_test_control_reg(pipe->ar_usb, BES_USB_FW_TX_DONE)) + bes2600_usb_set_status_reg(pipe->ar_usb, BES_USB_FW_TX_DONE); + + // notify tx done event + bes2600_usb_irq_handler(pipe->ar_usb); + + /* note: queue implements a lock */ + //skb_queue_tail(&pipe->io_comp_queue, skb); + //schedule_work(&pipe->io_complete_work); +} +int wsm_release_tx_buffer(struct bes2600_common *priv, int count); +void bes2600_bh_wakeup(struct bes2600_common *hw_priv); + +void bes2600_usb_rx_complete(struct bes2600_common *priv, struct sk_buff *skb) +{ + static u8 data[1600]; + static u32 cnt = 0; + + struct wsm_hdr *wsm; + size_t wsm_len; + u16 wsm_id; + u8 wsm_seq; + u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */ + + wsm = (struct wsm_hdr *)skb->data; + wsm_len = __le16_to_cpu(wsm->len); + if (WARN_ON(wsm_len > skb->len)) + goto err; + + if (priv->wsm_enable_wsm_dumps) + bes2600_dbg_dump(BES2600_DBG_SPI, "<--", skb->data, wsm_len); + + wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb, wsm_len); + + if (wsm_id == 0x0800) { + wsm_handle_exception(priv, + &skb->data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + bes2600_err(BES2600_DBG_SYS, "wsm exception.!\n"); + goto err; + } else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) { + bes2600_err(BES2600_DBG_SYS, "seq error %u. %u. 0x%x.", wsm_seq, priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id); + goto err; + } + priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7; + + if (IS_DRIVER_TO_MCU_CMD(wsm_id)) + confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label); + + if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) { + int rc = wsm_release_tx_buffer(priv, 1); + if (WARN_ON(rc < 0)) + return; + } + + bes2600_bh_wakeup(priv); + + /* bes2600_wsm_rx takes care on SKB livetime */ + //if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb))) + if ((wsm_handle_rx(priv, wsm_id, wsm, &skb))) + goto err; + + if (skb) { + dev_kfree_skb(skb); + skb = NULL; + } + return; +err: + if (skb) { + dev_kfree_skb(skb); + skb = NULL; + } + + if(cnt++ >= 0) + priv->sbus_ops->pipe_send(priv->sbus_priv, BES2600_USB_PIPE_TX_WLAN, 200, data); + return; +} + +void bes2600_tx_complete(struct bes2600_common *ar, struct sk_buff *skb) +{ + //bes2600_htc_tx_complete(ar, skb); +} +EXPORT_SYMBOL(bes2600_tx_complete); + +void bes2600_rx_complete(struct bes2600_common *ar, struct sk_buff *skb, u8 pipe) +{ + //bes2600_htc_rx_complete(ar, skb, pipe); + bes2600_usb_rx_complete(ar, skb); +} +EXPORT_SYMBOL(bes2600_rx_complete); + + +static void bes2600_usb_io_comp_work(struct work_struct *work) +{ + struct bes2600_usb_pipe *pipe = container_of(work, + struct bes2600_usb_pipe, + io_complete_work); + struct sbus_priv *ar_usb; + struct sk_buff *skb; + + ar_usb = pipe->ar_usb; + + while ((skb = skb_dequeue(&pipe->io_comp_queue))) { + if (pipe->flags & BES2600_USB_PIPE_FLAG_TX) { + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb xmit callback buf:0x%p\n", skb); + bes2600_tx_complete(ar_usb->ar, skb); + } else { + bes2600_dbg(BES2600_DBG_USB, + "bes2600 usb recv callback buf:0x%p\n", skb); + bes2600_rx_complete(ar_usb->ar, skb, + pipe->logical_pipe_num); + } + } +} + +#define BES2600_USB_MAX_DIAG_CMD (sizeof(struct bes2600_usb_ctrl_diag_cmd_write)) +#define BES2600_USB_MAX_DIAG_RESP (sizeof(struct bes2600_usb_ctrl_diag_resp_read)) + +static int bes2600_usb_send(struct sbus_priv *self, u8 PipeID, u32 len, u8 *data) +{ + struct sbus_priv *device = self; + struct bes2600_usb_pipe *pipe = &device->pipes[PipeID]; + struct bes2600_urb_context *urb_context; + int usb_status, status = 0; + struct urb *urb; + int send_cnt = atomic_read(&pipe->urb_pending_cnt); + + bes2600_dbg(BES2600_DBG_USB, "+%s pipe : %d, buf:0x%p, send_cnt:%d.\n", + __func__, PipeID, data, send_cnt); + + + urb_context = bes2600_usb_alloc_urb_from_pipe(pipe); + + if (urb_context == NULL) { + /* + * TODO: it is possible to run out of urbs if + * 2 endpoints map to the same pipe ID + */ + bes2600_err(BES2600_DBG_USB, "%s pipe:%d no urbs left. URB Cnt : %d\n", + __func__, PipeID, pipe->urb_cnt); + status = -ENOMEM; + goto fail_hif_send; + } + + urb_context->skb = (struct sk_buff *)data; + urb = urb_context->urb; + BUG_ON(!urb); + usb_fill_bulk_urb(urb, + device->udev, + pipe->usb_pipe_handle, + data, + len, + bes2600_usb_transmit_complete, urb_context); + + if ((len % pipe->max_packet_size) == 0) { + /* hit a max packet boundary on this pipe */ + urb->transfer_flags |= URB_ZERO_PACKET; + } + + bes2600_dbg(BES2600_DBG_USB, + "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->ep_address, len); + + usb_anchor_urb(urb, &pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + + atomic_add(1, &pipe->urb_pending_cnt); + + if (usb_status) { + bes2600_err(BES2600_DBG_USB, + "bes2600 usb : usb bulk transmit failed %d\n", + usb_status); + usb_unanchor_urb(urb); + bes2600_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + atomic_sub(1, &pipe->urb_pending_cnt); + status = -EINVAL; + } +fail_hif_send: + return status; +} + +static void * bes2600_usb_read(struct sbus_priv *self) +{ + return (void *)bes2600_recv_buf_get(self); +} + +#if 0 + +static void hif_stop(struct bes2600_common *ar) +{ + struct sbus_priv *device = bes2600_usb_priv(ar); + + bes2600_usb_flush_all(device); +} + +static void hif_detach_htc(struct bes2600_common *ar) +{ + struct sbus_priv *device = bes2600_usb_priv(ar); + + bes2600_usb_flush_all(device); +} + +/* exported hif usb APIs for htc pipe */ +static void hif_start(struct bes2600_common *ar) +{ + struct sbus_priv *device = bes2600_usb_priv(ar); + int i; + + bes2600_usb_start_recv_pipes(device); + + /* set the TX resource avail threshold for each TX pipe */ + for (i = BES2600_USB_PIPE_TX_CTRL; + i <= BES2600_USB_PIPE_BT_TX_DATA; i++) { + device->pipes[i].urb_cnt_thresh = + device->pipes[i].urb_alloc / 2; + } +} + +static int bes2600_usb_power_on(struct bes2600_common *ar) +{ + hif_start(ar); + return 0; +} + +static int bes2600_usb_power_off(struct bes2600_common *ar) +{ + hif_detach_htc(ar); + return 0; +} +#endif +static void bes2600_usb_destroy(struct sbus_priv *ar_usb) +{ + bes2600_usb_flush_all(ar_usb); + + bes2600_usb_cleanup_pipe_resources(ar_usb); + + usb_set_intfdata(ar_usb->interface, NULL); + + kfree(ar_usb->diag_cmd_buffer); + kfree(ar_usb->diag_resp_buffer); + + kfree(ar_usb); +} + +static struct sbus_priv *bes2600_usb_create(struct usb_interface *interface) +{ + struct usb_device *dev = interface_to_usbdev(interface); + struct sbus_priv *ar_usb; + struct bes2600_usb_pipe *pipe; + int status = 0; + int i; + + ar_usb = kzalloc(sizeof(struct sbus_priv), GFP_KERNEL); + if (ar_usb == NULL) + goto fail_bes2600_usb_create; + + usb_set_intfdata(interface, ar_usb); + spin_lock_init(&(ar_usb->cs_lock)); + ar_usb->udev = dev; + ar_usb->interface = interface; + ar_usb->ar = NULL; + + for (i = 0; i < BES2600_USB_PIPE_MAX; i++) { + if((i != BES2600_USB_PIPE_RX_BT) && + (i != BES2600_USB_PIPE_TX_BT)){ + pipe = &ar_usb->pipes[i]; + INIT_WORK(&pipe->io_complete_work, + bes2600_usb_io_comp_work); + skb_queue_head_init(&pipe->io_comp_queue); + } + } + + spin_lock_init(&ar_usb->rx_queue_lock); + skb_queue_head_init(&ar_usb->rx_queue); + + spin_lock_init(&ar_usb->status_lock); + ar_usb->usb_irq_handler = NULL; + ar_usb->irq_data = NULL; + ar_usb->int_control_reg = 0; + ar_usb->int_status_reg = 0; + + ar_usb->diag_cmd_buffer = kzalloc(BES2600_USB_MAX_DIAG_CMD, GFP_KERNEL); + if (ar_usb->diag_cmd_buffer == NULL) { + status = -ENOMEM; + goto fail_bes2600_usb_create; + } + + ar_usb->diag_resp_buffer = kzalloc(BES2600_USB_MAX_DIAG_RESP, + GFP_KERNEL); + if (ar_usb->diag_resp_buffer == NULL) { + status = -ENOMEM; + goto fail_bes2600_usb_create; + } + + status = bes2600_usb_setup_pipe_resources(ar_usb); + +fail_bes2600_usb_create: + if (status != 0) { + bes2600_usb_destroy(ar_usb); + ar_usb = NULL; + } + return ar_usb; +} +void bes2600_core_release(struct bes2600_common *self); + +static void bes2600_usb_device_detached(struct usb_interface *interface) +{ + struct sbus_priv *self = usb_get_intfdata(interface); + if (self) { + if (self->ar) { + bes2600_core_release(self->ar); + self->ar = NULL; + } + bes2600_usb_destroy(self); + } +} + +#if 0 +static void bes2600_usb_get_default_pipe(struct bes2600_common *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + *ul_pipe = BES2600_USB_PIPE_TX_CTRL; + *dl_pipe = BES2600_USB_PIPE_RX_CTRL; +} + +static int bes2600_usb_map_service_pipe(struct bes2600_common *ar, u16 svc_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + int status = 0; + + switch (svc_id) { + case HTC_CTRL_RSVD_SVC: + case WMI_CONTROL_SVC: + *ul_pipe = BES2600_USB_PIPE_TX_CTRL; + /* due to large control packets, shift to data pipe */ + *dl_pipe = BES2600_USB_PIPE_RX_DATA; + break; + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + *ul_pipe = BES2600_USB_PIPE_TX_DATA_LP; + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = BES2600_USB_PIPE_RX_DATA; + break; + case WMI_DATA_VI_SVC: + + if (test_bit(BES2600_FW_CAPABILITY_MAP_LP_ENDPOINT, + ar->fw_capabilities)) + *ul_pipe = BES2600_USB_PIPE_TX_DATA_LP; + else + *ul_pipe = BES2600_USB_PIPE_TX_DATA_MP; + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = BES2600_USB_PIPE_RX_DATA; + break; + case WMI_DATA_VO_SVC: + + if (test_bit(BES2600_FW_CAPABILITY_MAP_LP_ENDPOINT, + ar->fw_capabilities)) + *ul_pipe = BES2600_USB_PIPE_TX_DATA_LP; + else + *ul_pipe = BES2600_USB_PIPE_TX_DATA_MP; + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = BES2600_USB_PIPE_RX_DATA; + break; + default: + status = -EPERM; + break; + } + + return status; +} + +static u16 bes2600_usb_get_free_queue_number(struct bes2600_common *ar, u8 pipe_id) +{ + struct sbus_priv *device = bes2600_usb_priv(ar); + + return device->pipes[pipe_id].urb_cnt; +} + +static int bes2600_usb_submit_ctrl_out(struct sbus_priv *ar_usb, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmemdup(data, size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } + + /* note: if successful returns number of bytes transfered */ + ret = usb_control_msg(ar_usb->udev, + usb_sndctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 1000); + + if (ret < 0) { + bes2600_warn("Failed to submit usb control message: %d\n", ret); + kfree(buf); + return ret; + } + + kfree(buf); + + return 0; +} + +static int bes2600_usb_submit_ctrl_in(struct sbus_priv *ar_usb, + u8 req, u16 value, u16 index, void *data, + u32 size) +{ + u8 *buf = NULL; + int ret; + + if (size > 0) { + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } + + /* note: if successful returns number of bytes transfered */ + ret = usb_control_msg(ar_usb->udev, + usb_rcvctrlpipe(ar_usb->udev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, buf, + size, 2 * HZ); + + if (ret < 0) { + bes2600_warn("Failed to read usb control message: %d\n", ret); + kfree(buf); + return ret; + } + + memcpy((u8 *) data, buf, size); + + kfree(buf); + + return 0; +} + +static int bes2600_usb_ctrl_msg_exchange(struct sbus_priv *ar_usb, + u8 req_val, u8 *req_buf, u32 req_len, + u8 resp_val, u8 *resp_buf, u32 *resp_len) +{ + int ret; + + /* send command */ + ret = bes2600_usb_submit_ctrl_out(ar_usb, req_val, 0, 0, + req_buf, req_len); + + if (ret != 0) + return ret; + + if (resp_buf == NULL) { + /* no expected response */ + return ret; + } + + /* get response */ + ret = bes2600_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0, + resp_buf, *resp_len); + + return ret; +} + +static int bes2600_usb_diag_read32(struct bes2600_common *ar, u32 address, u32 *data) +{ + struct sbus_priv *ar_usb = ar->sbus_priv; + struct bes2600_usb_ctrl_diag_resp_read *resp; + struct bes2600_usb_ctrl_diag_cmd_read *cmd; + u32 resp_len; + int ret; + + cmd = (struct bes2600_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer; + + memset(cmd, 0, sizeof(*cmd)); + cmd->cmd = BES2600_USB_CTRL_DIAG_CC_READ; + cmd->address = cpu_to_le32(address); + resp_len = sizeof(*resp); + + ret = bes2600_usb_ctrl_msg_exchange(ar_usb, + BES2600_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(struct bes2600_usb_ctrl_diag_cmd_write), + BES2600_USB_CONTROL_REQ_DIAG_RESP, + ar_usb->diag_resp_buffer, &resp_len); + + if (ret) { + bes2600_warn("diag read32 failed: %d\n", ret); + return ret; + } + + resp = (struct bes2600_usb_ctrl_diag_resp_read *) + ar_usb->diag_resp_buffer; + + *data = le32_to_cpu(resp->value); + + return ret; +} + +static int bes2600_usb_diag_write32(struct bes2600_common *ar, u32 address, __le32 data) +{ + struct sbus_priv *ar_usb = ar->sbus_priv; + struct bes2600_usb_ctrl_diag_cmd_write *cmd; + int ret; + + cmd = (struct bes2600_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer; + + memset(cmd, 0, sizeof(struct bes2600_usb_ctrl_diag_cmd_write)); + cmd->cmd = cpu_to_le32(BES2600_USB_CTRL_DIAG_CC_WRITE); + cmd->address = cpu_to_le32(address); + cmd->value = data; + + ret = bes2600_usb_ctrl_msg_exchange(ar_usb, + BES2600_USB_CONTROL_REQ_DIAG_CMD, + (u8 *) cmd, + sizeof(*cmd), + 0, NULL, NULL); + if (ret) { + bes2600_warn("diag_write32 failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int bes2600_usb_bmi_read(struct bes2600_common *ar, u8 *buf, u32 len) +{ + struct sbus_priv *ar_usb = ar->sbus_priv; + int ret; + + /* get response */ + ret = bes2600_usb_submit_ctrl_in(ar_usb, + BES2600_USB_CONTROL_REQ_RECV_BMI_RESP, + 0, 0, buf, len); + if (ret) { + bes2600_err("Unable to read the bmi data from the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static int bes2600_usb_bmi_write(struct bes2600_common *ar, u8 *buf, u32 len) +{ + struct sbus_priv *ar_usb = ar->sbus_priv; + int ret; + + /* send command */ + ret = bes2600_usb_submit_ctrl_out(ar_usb, + BES2600_USB_CONTROL_REQ_SEND_BMI_CMD, + 0, 0, buf, len); + if (ret) { + bes2600_err("unable to send the bmi data to the device: %d\n", + ret); + return ret; + } + + return 0; +} + +static void bes2600_usb_stop(struct bes2600_common *ar) +{ + hif_stop(ar); +} + +static void bes2600_usb_cleanup_scatter(struct bes2600_common *ar) +{ + /* + * USB doesn't support it. Just return. + */ + return; +} + +static int bes2600_usb_suspend(struct bes2600_common *ar, struct cfg80211_wowlan *wow) +{ + /* + * cfg80211 suspend/WOW currently not supported for USB. + */ + return 0; +} + +static int bes2600_usb_resume(struct bes2600_common *ar) +{ + /* + * cfg80211 resume currently not supported for USB. + */ + return 0; +} +#endif + +static int bes2600_usb_init(struct sbus_priv *self, struct bes2600_common *ar) +{ + int queue_empty = -1; + + self->ar = ar; + spin_lock_bh(&self->status_lock); + self->int_status_reg = 0; + self->int_control_reg = 0; + spin_unlock_bh(&self->status_lock); + + spin_lock_bh(&self->rx_queue_lock); + queue_empty = skb_queue_empty(&self->rx_queue); + spin_unlock_bh(&self->rx_queue_lock); + + /* revoke rx work if firmware ready arrived before bes2600 probe done */ + if(!queue_empty) { + bes2600_irq_handler(ar); + } + + return 0; +} + +static int bes2600_usb_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + return 0; +} + +static int bes2600_usb_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return 0; +} + +static void bes2600_usb_lock(struct sbus_priv *self) +{ +} + +static void bes2600_usb_unlock(struct sbus_priv *self) +{ +} + +static size_t bes2600_usb_align_size(struct sbus_priv *self, size_t size) +{ + return size; +} + +int bes2600_usb_irq_subscribe(struct sbus_priv *self, sbus_irq_handler handler, void *priv) +{ + spin_lock_bh(&self->status_lock); + self->usb_irq_handler = handler; + self->irq_data = priv; + spin_unlock_bh(&self->status_lock); + return 0; +} + +int bes2600_usb_irq_unsubscribe(struct sbus_priv *self) +{ + spin_lock_bh(&self->status_lock); + self->usb_irq_handler = NULL; + self->irq_data = NULL; + spin_unlock_bh(&self->status_lock); + return 0; +} + +int bes2600_usb_reset(struct sbus_priv *self) +{ + self->ar = NULL; + spin_lock_bh(&self->status_lock); + self->int_status_reg = 0; + self->int_control_reg = 0; + spin_unlock_bh(&self->status_lock); + return 0; +} + +int bes2600_usb_set_block_size(struct sbus_priv *self, size_t size) +{ + return 0; +} + +static int bes2600_usb_reg_read(struct sbus_priv *self, u32 reg, void *dst, int count) +{ + int ret = 0; + unsigned long flags; + if(reg == BES_USB_CONTROL_REG) { + spin_lock_irqsave(&self->status_lock,flags); + *((u32 *)dst) = self->int_control_reg; + spin_unlock_irqrestore(&self->status_lock,flags); + } + else if(reg == BES_USB_STATUS_REG) { + spin_lock_irqsave(&self->status_lock,flags); + *((u32 *)dst) = self->int_status_reg; + spin_unlock_irqrestore(&self->status_lock,flags); + } + else + ret = -EINVAL; + + return ret; +} + +static int bes2600_usb_reg_write(struct sbus_priv *self, u32 reg, const void *src, int count) +{ + int ret = 0; + unsigned long flags; + if(reg == BES_USB_CONTROL_REG) { + spin_lock_irqsave(&self->status_lock,flags); + self->int_control_reg = *((u32 *)src); + spin_unlock_irqrestore(&self->status_lock,flags); + } + else if(reg == BES_USB_STATUS_REG) { + spin_lock_irqsave(&self->status_lock,flags); + self->int_status_reg = *((u32 *)src); + spin_unlock_irqrestore(&self->status_lock,flags); + } + else + ret = -EINVAL; + + return ret; +} + +static struct sbus_ops bes2600_usb_ops = { + #if 0 + .diag_read32 = bes2600_usb_diag_read32, + .diag_write32 = bes2600_usb_diag_write32, + .bmi_read = bes2600_usb_bmi_read, + .bmi_write = bes2600_usb_bmi_write, + .power_on = bes2600_usb_power_on, + .power_off = bes2600_usb_power_off, + .stop = bes2600_usb_stop, + + .pipe_get_default = bes2600_usb_get_default_pipe, + .pipe_map_service = bes2600_usb_map_service_pipe, + .pipe_get_free_queue_number = bes2600_usb_get_free_queue_number, + .cleanup_scatter = bes2600_usb_cleanup_scatter, + .suspend = bes2600_usb_suspend, + .resume = bes2600_usb_resume, + #endif + .init = bes2600_usb_init, + .sbus_memcpy_fromio = bes2600_usb_memcpy_fromio, + .sbus_memcpy_toio = bes2600_usb_memcpy_toio, + .lock = bes2600_usb_lock, + .unlock = bes2600_usb_unlock, + .irq_subscribe = bes2600_usb_irq_subscribe, + .irq_unsubscribe = bes2600_usb_irq_unsubscribe, + .reset = bes2600_usb_reset, + .align_size = bes2600_usb_align_size, + .set_block_size = bes2600_usb_set_block_size, + .pipe_send = bes2600_usb_send, + .pipe_read = bes2600_usb_read, + .sbus_reg_read = bes2600_usb_reg_read, + .sbus_reg_write = bes2600_usb_reg_write, +}; + + +/* bes2600 usb driver registered functions */ +static int bes2600_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *dev = NULL; + struct bes2600_common *ar; + struct sbus_priv *self = NULL; + int vendor_id, product_id; + int ret = 0; + + bes2600_chrdev_update_signal_mode(); + + dev = interface_to_usbdev(interface); + usb_get_dev(dev); + + vendor_id = le16_to_cpu(dev->descriptor.idVendor); + product_id = le16_to_cpu(dev->descriptor.idProduct); + bes2600_dbg(BES2600_DBG_USB, "vendor_id = %04x\n", vendor_id); + bes2600_dbg(BES2600_DBG_USB, "product_id = %04x\n", product_id); + + if (interface->cur_altsetting) + bes2600_dbg(BES2600_DBG_USB, "USB Interface %d\n", + interface->cur_altsetting->desc.bInterfaceNumber); + + + if (dev->speed == USB_SPEED_HIGH) + bes2600_dbg(BES2600_DBG_USB, "USB 2.0 Host\n"); + else + bes2600_dbg(BES2600_DBG_USB, "USB 1.1 Host\n"); + + self = bes2600_usb_create(interface); + + if (self == NULL) { + ret = -ENOMEM; + bes2600_dbg(BES2600_DBG_USB, "USB create failed!\n"); + goto err_usb_put; + } + + bes2600_usb_start_recv_pipes(self); + + bes2600_chrdev_update_signal_mode(); + bes2600_dbg(BES2600_DBG_USB, "%s type:%d sig_mode:%d\n", __func__, + bes2600_chrdev_get_fw_type(), bes2600_chrdev_is_signal_mode()); + + //bes2600_reg_set_object(&bes2600_usb_ops, self); + if ((ret = bes2600_load_firmware(&bes2600_usb_ops, self)) < 0) { + bes2600_err(BES2600_DBG_USB, "bes2600_load_firmware failed(%d)\n", ret); + goto err_core_free; + } + + ret = bes2600_core_probe(&bes2600_usb_ops, (struct sbus_priv*)self, &self->udev->dev, &ar); + if (ret) { + bes2600_err(BES2600_DBG_USB, "Failed to init bes2600 core: %d\n", ret); + goto err_core_free; + } + + return ret; + +err_core_free: +#ifdef CONFIG_BES2600_BT + bes2600_btusb_uninit(interface); +#endif + bes2600_usb_destroy(self); +err_usb_put: + usb_put_dev(dev); + + return ret; +} + +int bes2600_register_net_dev(struct sbus_priv *bus_priv) +{ + int status = 0; + BUG_ON(!bus_priv); + status = bes2600_core_probe(&bes2600_usb_ops, + bus_priv, &bus_priv->udev->dev, &bus_priv->ar); + return status; +} + +int bes2600_unregister_net_dev(struct sbus_priv *bus_priv) +{ + BUG_ON(!bus_priv); + if (bus_priv->ar) { + bes2600_core_release(bus_priv->ar); + bus_priv->ar = NULL; + } + return 0; +} + +bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv) +{ + BUG_ON(!bus_priv); + return (bus_priv->ar != NULL); +} + +static void bes2600_usb_remove(struct usb_interface *interface) +{ +#ifdef CONFIG_BES2600_BT + bes2600_btusb_uninit(interface); +#endif + usb_put_dev(interface_to_usbdev(interface)); + bes2600_usb_device_detached(interface); +} + +#ifdef CONFIG_PM +static int bes2600_usb_pm_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct sbus_priv *device; + device = usb_get_intfdata(interface); + + bes2600_usb_flush_all(device); + return 0; +} +static int bes2600_usb_pm_resume(struct usb_interface *interface) +{ + struct sbus_priv *device; + device = usb_get_intfdata(interface); + + bes2600_usb_post_recv_transfers(&device->pipes[BES2600_USB_PIPE_RX_WLAN], + BES2600_USB_RX_BUFFER_SIZE); + + return 0; +} +#else +#define bes2600_usb_pm_suspend NULL +#define bes2600_usb_pm_resume NULL +#endif + +/* table of devices that work with this driver */ +static const struct usb_device_id bes2600_usb_ids[] = { + {USB_DEVICE(0xBE57, 0x0104)}, + {USB_DEVICE(0xBE57, 0x2002)}, + {USB_DEVICE(0xBE57, 0x2003)}, + { /* Terminating entry */ }, +}; + +MODULE_DEVICE_TABLE(usb, bes2600_usb_ids); + + + +static struct usb_driver bes2600_usb_driver = { + .name = "bes2600_usb", + .probe = bes2600_usb_probe, + .suspend = bes2600_usb_pm_suspend, + .resume = bes2600_usb_pm_resume, + .disconnect = bes2600_usb_remove, + .id_table = bes2600_usb_ids, + .supports_autosuspend = true, + .disable_hub_initiated_lpm = 1, +}; + +static int __init bes2600_usb_module_init(void) +{ + bes2600_info(BES2600_DBG_USB, "------Driver: bes2600.ko version :%s\n", BES2600_DRV_VERSION); + return usb_register_driver(&bes2600_usb_driver , THIS_MODULE, "BES2600"); +} + +static void __exit bes2600_usb_module_exit(void) +{ + usb_deregister(&bes2600_usb_driver); +} + +module_init(bes2600_usb_module_init); +module_exit(bes2600_usb_module_exit); + +MODULE_AUTHOR("Bestechnic, Inc."); +MODULE_DESCRIPTION("Driver support for BEST2000 wireless USB devices"); +MODULE_LICENSE("Dual BSD/GPL"); + + + + diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c new file mode 100644 index 000000000000..77573e59952e --- /dev/null +++ b/drivers/staging/bes2600/bes_chardev.c @@ -0,0 +1,1280 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bes2600.h" +#include "sbus.h" +#include "hwio.h" +#include "fwio.h" +#include "bes_chardev.h" +#include "tx_loop.h" + +enum wait_state { + BES2600_BOOT_WAIT_NONE = 0, + BES2600_BOOT_WAIT_PROBE_DONE, + BES2600_BOOT_WAIT_CLOSE, +}; + +enum bus_probe_state { + BES2600_BUS_PROBE_NONE = 0, + BES2600_BUS_PROBE_START, + BES2600_BUS_PROBE_OK, + BES2600_BUS_PROBE_TIMEOUT, +}; + +struct bes_cdev +{ + struct cdev cdev; + dev_t dev_id; + int major; + int minor; + struct class *class; + struct device *device; + atomic_t num_proc; + wait_queue_head_t open_wq; + spinlock_t status_lock; + bool wifi_opened; + bool bt_opened; + bool bton_pending; + bool dpd_calied; + u8 *dpd_data; + u32 dpd_len; + enum wait_state wait_state; + wait_queue_head_t probe_done_wq; + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; + bool sig_mode; + int fw_type; + bool bus_error; + bool halt_dev; + struct delayed_work probe_timeout_work; + enum bus_probe_state bus_probe; + struct work_struct wifi_force_close_work; +#ifdef BES2600_WRITE_DPD_TO_FILE + int no_dpd; +#endif + enum pend_read_op read_flag; + u32 wakeup_state; /* for userspace check wakeup reason */ +#ifdef BES2600_DUMP_FW_DPD_LOG + u8 *dpd_log; + u16 dpd_log_len; +#endif +}; + +struct bes2600_op_map +{ + char op[20]; // operation + int op_len; // operation length, used for effiency + int (*handler) (const char *cmd); // handler +}; + +static struct bes_cdev bes2600_cdev; +module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644); +#ifdef BES2600_WRITE_DPD_TO_FILE +module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644); +#endif + +extern int bes2600_register_net_dev(struct sbus_priv *bus_priv); +extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv); +extern bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv); + +static bool bes2600_bootup_end(void) +{ + bool end; + + spin_lock(&bes2600_cdev.status_lock); + end = (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_TIMEOUT || + bes2600_cdev.sbus_priv != NULL || + bes2600_cdev.bus_error); + spin_unlock(&bes2600_cdev.status_lock); + + return end; +} + +static int bes2600_chrdev_switch_subsys(int wake_flag, int subsys, bool active) +{ + int ret = 0; + + if(bes2600_cdev.sbus_priv == NULL) + return -EFAULT; + + if(active) { + if(bes2600_cdev.sbus_ops->gpio_wake) + bes2600_cdev.sbus_ops->gpio_wake(bes2600_cdev.sbus_priv, wake_flag); + + if(bes2600_cdev.sbus_ops->sbus_active) + ret = bes2600_cdev.sbus_ops->sbus_active(bes2600_cdev.sbus_priv, subsys); + + if(bes2600_cdev.sbus_ops->gpio_sleep) + bes2600_cdev.sbus_ops->gpio_sleep(bes2600_cdev.sbus_priv, wake_flag); + } else { + if(bes2600_cdev.sbus_ops->gpio_wake) + bes2600_cdev.sbus_ops->gpio_wake(bes2600_cdev.sbus_priv, wake_flag); + + if(bes2600_cdev.sbus_ops->sbus_deactive) + ret = bes2600_cdev.sbus_ops->sbus_deactive(bes2600_cdev.sbus_priv, subsys); + + if(bes2600_cdev.sbus_ops->gpio_sleep) + bes2600_cdev.sbus_ops->gpio_sleep(bes2600_cdev.sbus_priv, wake_flag); + } + + return ret; +} + +static int bes2600_switch_wifi(bool on) +{ + int ret = 0; + long status = 0; + + if(bes2600_cdev.wifi_opened == on) + return 0; + + if(on) { + if(bes2600_chrdev_check_system_close()) { + bes2600_info(BES2600_DBG_CHARDEV, "power up bes2600 when active wifi.\n"); + /* reset bus error status when restart bes2600 */ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.bus_error = false; + bes2600_cdev.halt_dev = false; + bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE; + spin_unlock(&bes2600_cdev.status_lock); + + /* power up bes2600, trigger system to execute probe function */ + bes2600_cdev.wifi_opened = true; + bes2600_cdev.sbus_ops->power_switch(NULL, 1); + + /* wait probe done event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + bes2600_bootup_end(), HZ * 8); + WARN_ON(status <= 0); + ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0; + } else { + /* bes2600 is already powered up, we just need to create net device */ + if(bes2600_cdev.sbus_priv) { + if(!bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) { + ret = bes2600_register_net_dev(bes2600_cdev.sbus_priv); + } + } else { + ret = -EFAULT; + } + } + } else { + if(bes2600_cdev.sbus_priv && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) { + bes2600_unregister_net_dev(bes2600_cdev.sbus_priv); + } + } + + if(!ret) { + bes2600_cdev.wifi_opened = on; + } else { + bes2600_cdev.wifi_opened = false; + bes2600_info_with_cond(on, BES2600_DBG_CHARDEV, "open wifi failed\n"); + } + + return ret; +} + +static int bes2600_switch_bt(bool on) +{ + int ret = 0; + long status = 0; + + if(bes2600_cdev.bt_opened == on) + return 0; + + if(on) { + if(bes2600_chrdev_check_system_close()) { + bes2600_info(BES2600_DBG_CHARDEV, "power up bes2600 when active bt.\n"); + /* reset bus error status when restart bes2600 */ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.bus_error = false; + bes2600_cdev.halt_dev = false; + bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE; + spin_unlock(&bes2600_cdev.status_lock); + + /* set opend state in advance */ + bes2600_cdev.bt_opened = true; + bes2600_cdev.bton_pending = true; + + /* power up bes2600, trigger system to execute probe function */ + bes2600_cdev.sbus_ops->power_switch(NULL, 1); + + /* wait bootup process end */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + bes2600_bootup_end(), HZ * 8); + WARN_ON(status <= 0); + + /* check if there is a error when bootup */ + ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0; + } else { + bes2600_info(BES2600_DBG_CHARDEV, "bes2600 activate bt.\n"); + ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true); + } + } else { + bes2600_info(BES2600_DBG_CHARDEV, "bes2600 deactivate bt.\n"); + bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false); + } + + if(!ret) { + bes2600_cdev.bt_opened = on; + } else { + bes2600_cdev.bt_opened = false; + bes2600_cdev.bton_pending = false; + bes2600_info_with_cond(ret, BES2600_DBG_CHARDEV, "open bt failed\n"); + } + + return ret; +} + +static int bes2600_get_cmd_and_ifname(const char *str, char **result) +{ + int cmd_len = 0; + int ifname_len = 0; + char *sp = NULL; + char *tmp_ptr = NULL; + char *cmd_ptr = NULL; + + /* check if input arguments is valid */ + if(!str || strncmp(str, "ifname:", 7) != 0) + return -1; + + sp = strchr(str, ' '); + if(strncmp(sp + 1, "cmd:", 4) != 0) + return -1; + + /* extract interface name */ + ifname_len = sp - str - 7; + tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL); + if(!tmp_ptr) { + return -2; + } + + strncpy(tmp_ptr, str+7, ifname_len); + tmp_ptr[ifname_len] = '\0'; + result[0] = tmp_ptr; + + /* get command length */ + cmd_ptr = strstr(str, "cmd:"); + cmd_ptr += 4; + sp = strchr(cmd_ptr, ' '); + if(!sp) { /* the command don't have any parameter */ + cmd_len = strlen(cmd_ptr); + if(cmd_ptr[cmd_len - 1] == '\n') + --cmd_len; + } else { /* the command have one or more parameter */ + cmd_len = sp - cmd_ptr; + } + + /* copy command to out buffer */ + tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL); + if(!tmp_ptr) { + kfree(result[0]); + result[0] = NULL; + return -3; + } + + strncpy(tmp_ptr, cmd_ptr, cmd_len); + tmp_ptr[cmd_len] = '\0'; + result[1] = tmp_ptr; + + return 0; +} + +static void bes2600_recyle_cmd_and_ifname_mem(char **info) +{ + if(info[0]) { + kfree(info[0]); + info[0] = NULL; + } + + if(info[1]) { + kfree(info[1]); + info[1] = NULL; + } + +} + +static int bes2600_op_default_handler(const char *str) +{ + char *info[2] = {0}; + + if(bes2600_get_cmd_and_ifname(str, info) == 0) { + bes2600_info(BES2600_DBG_CHARDEV, "cmd(%s) on %s not handled\n", info[1], info[0]); + } else { + bes2600_err(BES2600_DBG_CHARDEV, "%s get command fail, the origin string is %s\n", __func__, str); + } + + bes2600_recyle_cmd_and_ifname_mem(info); + + return 0; +} + +static int bes2600_op_wifi_bt_on_off(const char *str) +{ + char *info[2] = {0}; + int ret = 0; + enum wait_state wait_state; + enum bus_probe_state probe_state; + unsigned long status = 0; + + spin_lock(&bes2600_cdev.status_lock); + probe_state = bes2600_cdev.bus_probe; + wait_state = bes2600_cdev.wait_state; + spin_unlock(&bes2600_cdev.status_lock); + + /* only work for wifi signal mode */ + if(bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL) + return -EFAULT; + + /* wait bus probe operation end */ + if(probe_state == BES2600_BUS_PROBE_START) { + bes2600_info(BES2600_DBG_CHARDEV, "wait bus probe operation end\n"); + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START), + HZ); + WARN_ON(status <= 0); + } + + /* must wait previous operation end in critical section */ + if(wait_state != BES2600_BOOT_WAIT_NONE) { + bes2600_info(BES2600_DBG_CHARDEV, "wait previous operation end\n"); + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE), + HZ * 8); + WARN_ON(status <= 0); + } + + /* if dpd calibration is doing, modify wifi and bt state directly */ + spin_lock(&bes2600_cdev.status_lock); + if(bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) { + if(bes2600_get_cmd_and_ifname(str, info) == 0) { + if(strncmp(info[1], "WIFI_ON", 7) == 0) { + bes2600_cdev.wifi_opened = true; + } else if(strncmp(info[1], "WIFI_OFF", 8) == 0) { + bes2600_cdev.wifi_opened = false; + } else if(strncmp(info[1], "BT_ON", 5) == 0) { + bes2600_cdev.bt_opened = true; + bes2600_cdev.bton_pending = true; + } else if(strncmp(info[1], "BT_OFF", 6) == 0) { + bes2600_cdev.bt_opened = false; + bes2600_cdev.bton_pending = false; + } + } + bes2600_recyle_cmd_and_ifname_mem(info); + spin_unlock(&bes2600_cdev.status_lock); + + /* wait probe done event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + bes2600_bootup_end(), HZ * 8); + WARN_ON(status <= 0); + + return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0; + } + spin_unlock(&bes2600_cdev.status_lock); + + /* process wifi/bt on/off operation */ + if(bes2600_get_cmd_and_ifname(str, info) == 0) { + if(strncmp(info[1], "WIFI_ON", 7) == 0) { + ret = bes2600_switch_wifi(1); + } else if(strncmp(info[1], "WIFI_OFF", 8) == 0) { + ret = bes2600_switch_wifi(0); + } else if(strncmp(info[1], "BT_ON", 5) == 0) { + ret = bes2600_switch_bt(1); + } else if(strncmp(info[1], "BT_OFF", 6) == 0) { + ret = bes2600_switch_bt(0); + } + } + + if(!ret && bes2600_chrdev_check_system_close()) + ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, + bes2600_cdev.sbus_priv); + + bes2600_recyle_cmd_and_ifname_mem(info); + + return ret ; +} + + +static int bes2600_op_change_fw_type(const char *str) +{ + int ret = 0; + int temp = 0; + long status = 0; + char *cmd_ptr = NULL; + char fw_type[5] = {0}; + bool sys_closed = bes2600_chrdev_check_system_close(); + + bes2600_dbg(BES2600_DBG_CHARDEV, "%s is called, arg:%s\n", __func__, str); + + /* check if user input is valid */ + cmd_ptr = strstr(str, "CHANGE_FW_TYPE "); + if(strlen(str) < 16 || !cmd_ptr) { + bes2600_err(BES2600_DBG_CHARDEV, "the format of \"%s\" is error\n", str); + return -EINVAL; + } + + /* convert fw_type from string to int */ + strncpy(fw_type, cmd_ptr + 14, 4); + fw_type[0] = '+'; + ret = kstrtoint(fw_type, 10, &temp); + if(ret < 0) { + bes2600_err(BES2600_DBG_CHARDEV, "%s parse error\n", __func__); + return -EINVAL; + } + + /* no need to realod firmware if new fw_type is equal to the old */ + if(temp == bes2600_cdev.fw_type ) { + bes2600_info(BES2600_DBG_CHARDEV, "fw type is equal\n"); + return 0; + } + + /* close wifi net device */ + if(bes2600_cdev.sbus_priv + && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) { + bes2600_unregister_net_dev(bes2600_cdev.sbus_priv); + } + + /* update firmware type */ + bes2600_cdev.fw_type = temp; + bes2600_chrdev_update_signal_mode(); + + if(!sys_closed) { + /* close device to call disconnect function */ + bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0); + } + + /* wait disconnect event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10); + WARN_ON(status <= 0); + + + if(bes2600_cdev.dpd_calied + && bes2600_chrdev_check_system_close()) { + bes2600_info(BES2600_DBG_CHARDEV, "no need to reload firmware\n"); + return 0; + } + + + /* power on device to call probe function */ + bes2600_info(BES2600_DBG_CHARDEV, "reload firmware...\n"); + bes2600_cdev.sbus_ops->power_switch(NULL, 1); + /* wait probe done event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + bes2600_bootup_end(), HZ * 10); + WARN_ON(status <= 0); + + ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0; + + + return ret; +} + +static int bes2600_op_bt_wakeup(const char *str) +{ + int ret = 0; + unsigned long status = 0; + + spin_lock(&bes2600_cdev.status_lock); + if(!bes2600_cdev.bt_opened) { + spin_unlock(&bes2600_cdev.status_lock); + return -EFAULT; + } + spin_unlock(&bes2600_cdev.status_lock); + + /* wait probe done event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + bes2600_bootup_end(), HZ * 8); + if(status <= 0 || bes2600_chrdev_is_bus_error()) + return -EFAULT; + + bes2600_info(BES2600_DBG_CHARDEV, "bes2600 wakeup bt.\n"); + ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true); + + return ret; +} + +static int bes2600_op_bt_sleep(const char *str) +{ + int ret = 0; + unsigned long status = 0; + + spin_lock(&bes2600_cdev.status_lock); + if(!bes2600_cdev.bt_opened) { + spin_unlock(&bes2600_cdev.status_lock); + return -EFAULT; + } + spin_unlock(&bes2600_cdev.status_lock); + + /* wait probe done event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, + bes2600_bootup_end(), HZ * 8); + if(status <= 0 || bes2600_chrdev_is_bus_error()) + return -EFAULT; + + bes2600_info(BES2600_DBG_CHARDEV, "bes2600 allow bt sleep.\n"); + ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false); + + return ret; +} + +static int bes2600_op_set_wakeup_read_flag(const char *str) +{ + bes2600_dbg(BES2600_DBG_CHARDEV, "%s is called, arg:%s\n", __func__, str); + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE; + spin_unlock(&bes2600_cdev.status_lock); + + return 0; +} + +#ifdef FW_DOWNLOAD_UART_DAEMON +int bes2600_load_uevent(char *env[]) +{ + return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env); +} +#endif + +static struct bes2600_op_map bes2600_op_map_tab[] ={ + /*op op_len handler */ + {"P2P_SET_NOA", 11, bes2600_op_default_handler}, + {"P2P_SET_PS", 10, bes2600_op_default_handler}, + {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler}, + {"LINKSPEED", 9, bes2600_op_default_handler}, + {"RSSI", 4, bes2600_op_default_handler}, + {"GETBAND", 7, bes2600_op_default_handler}, + {"WLS_BATCHING", 12, bes2600_op_default_handler}, + {"MACADDR", 7, bes2600_op_default_handler}, + {"RXFILTER-START", 14, bes2600_op_default_handler}, + {"RXFILTER-STOP", 13, bes2600_op_default_handler}, + {"RXFILTER-ADD", 12, bes2600_op_default_handler}, + {"RXFILTER-REMOVE", 15, bes2600_op_default_handler}, + {"BTCOEXMODE", 10, bes2600_op_default_handler}, + {"BTCOEXSCAN-START", 16, bes2600_op_default_handler}, + {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler}, + {"SETSUSPENDMODE", 14, bes2600_op_default_handler}, + {"COUNTRY", 7, bes2600_op_default_handler}, + {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off}, + {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off}, + {"BT_ON", 5, bes2600_op_wifi_bt_on_off}, + {"BT_OFF", 6, bes2600_op_wifi_bt_on_off}, + {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type}, + {"BT_WAKEUP", 9, bes2600_op_bt_wakeup}, + {"BT_SLEEP", 8, bes2600_op_bt_sleep}, + {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag}, +}; + +static int bes2600_chrdev_check_system_close_internal(void) +{ + return (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) + &&(bes2600_cdev.bt_opened == false) + && (bes2600_cdev.wifi_opened == false); +} + +static int bes2600_chrdev_open(struct inode *inode, struct file *filp) +{ + if(atomic_read(&bes2600_cdev.num_proc) > 0) { + wait_event_timeout(bes2600_cdev.open_wq, + (atomic_read(&bes2600_cdev.num_proc) == 0), + MAX_SCHEDULE_TIMEOUT); + } + + bes2600_dbg(BES2600_DBG_CHARDEV, "bes2600 char device is opened\n"); + atomic_inc(&bes2600_cdev.num_proc); + + return 0; +} + +static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[64] = {0}; + unsigned int len; + + switch (bes2600_cdev.read_flag) { + case BES_CDEV_READ_WAKEUP_STATE: + len = sprintf(buf, "wakeup_state:%u\n", bes2600_cdev.wakeup_state); + break; + default: + len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n", + bes2600_cdev.dpd_calied, + bes2600_cdev.wifi_opened, + bes2600_cdev.bt_opened, + bes2600_cdev.fw_type); + break; + } + + len = sizeof(buf); + /* reset read flag */ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX; + spin_unlock(&bes2600_cdev.status_lock); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t bes2600_chrdev_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int i = 0; + int cmd_num = ARRAY_SIZE(bes2600_op_map_tab); + int cmd_len = 0; + int ret = 0; + char *info[2] = {0}; + char *buf = NULL; + + /* copy content from user space to kernel */ + /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */ + buf = kmalloc(count + 1, GFP_KERNEL); + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + /* add terminal character */ + buf[count] = '\0'; + + /* extract comand and interface */ + if(bes2600_get_cmd_and_ifname(buf, info) != 0) { + bes2600_err(BES2600_DBG_CHARDEV, "%s get command fail, the origin string is %s\n", __func__, buf); + kfree(buf); + return -EINVAL; + } + + /* match operation item and execure its handler */ + cmd_len = strlen(info[1]); + for(i = 0; i < cmd_num; i++) { + if(cmd_len < bes2600_op_map_tab[i].op_len) + continue; + + if(strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) { + ret = bes2600_op_map_tab[i].handler(buf); + break; + } + } + + /* operation item mismatch */ + if(i == cmd_num) { + bes2600_err(BES2600_DBG_CHARDEV, "cmd(%s) mismatch\n", info[1]); + } + + bes2600_recyle_cmd_and_ifname_mem(info); + kfree(buf); + + return (ret == 0) ? count : ret; +} + +static int bes2600_chrdev_release (struct inode *inode, struct file *file) +{ + if(atomic_dec_and_test(&bes2600_cdev.num_proc)) { + wake_up(&bes2600_cdev.open_wq); + } + + bes2600_dbg(BES2600_DBG_CHARDEV, "bes2600 char device is closed\n"); + + return 0; +} + +static struct file_operations bes2600_chardev_fops = +{ + .owner = THIS_MODULE, + .open = bes2600_chrdev_open, + .read = bes2600_chrdev_read, + .write = bes2600_chrdev_write, + .release = bes2600_chrdev_release, +}; + +#ifdef BES2600_WRITE_DPD_TO_FILE +static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size) +{ + int ret = 0; + struct file *fp; + + if(buffer == NULL || size == 0) + return 0; + + fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR); + if (IS_ERR(fp)) { + bes2600_err(BES2600_DBG_CHARDEV, "BES2600 : can't open %s\n",path); + return -1; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)) + ret = kernel_write(fp, buffer, size, &fp->f_pos); +#else + ret = kernel_write(fp, buffer, size, fp->f_pos); +#endif + bes2600_err_with_cond(ret < 0, BES2600_DBG_CHARDEV, "write dpd to file failed\n"); + + filp_close(fp,NULL); + + bes2600_info(BES2600_DBG_CHARDEV, "write dpd to %s\n", path); + + return ret; +} + +static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len) +{ + int ret = 0; + u32 read_len = 0; + u8* read_data = NULL; + u32 cal_crc = 0; + u32 dpd_crc = 0; + struct file *fp; + + /* open file */ + fp = filp_open(file, O_RDONLY, 0);//S_IRUSR + if (IS_ERR(fp)) { + bes2600_info(BES2600_DBG_CHARDEV, "BES2600 : can't open %s\n",file); + return -1; + } + +#ifdef BES2600_WRITE_DPD_TO_FILE + if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) { + bes2600_err(BES2600_DBG_CHARDEV, + "bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n", + fp->f_inode->i_size, DPD_BIN_FILE_SIZE); + filp_close(fp, NULL); + return -1; + } +#endif + + /* allocate memory for storing reading data */ + read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL); + if(read_data == NULL) { + bes2600_info(BES2600_DBG_CHARDEV, "%s alloc mem fail\n", __func__); + goto err1; + } + + /* read data from file */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)) + ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos); +#else + ret = kernel_read(fp, fp->f_pos, read_data, fp->f_inode->i_size); +#endif + if(ret < DPD_BIN_SIZE) { + bes2600_err(BES2600_DBG_CHARDEV, "%s read fail, ret=%d\n", __func__, ret); + goto err2; + } + + /* calculate crc value */ + read_len = DPD_BIN_SIZE; + dpd_crc = *((u32 *)read_data); + cal_crc ^= 0xffffffffL; + cal_crc = crc32_le(cal_crc, read_data + 4, read_len - 4); + cal_crc ^= 0xffffffffL; + + /* check if the dpd data is valid */ + if(cal_crc != dpd_crc) { + bes2600_err(BES2600_DBG_CHARDEV, + "bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n", + cal_crc, dpd_crc); + goto err2; + } + + /* close file */ + filp_close(fp, NULL); + + /* copy data to external */ + *data = read_data; + *len = read_len; + + /* output debug information */ + bes2600_info(BES2600_DBG_CHARDEV, "read dpd data from %s\n", file); + + return 0; + +err2: + kfree(read_data); +err1: + filp_close(fp, NULL); + *data = NULL; + *len = 0; + return -1; +} +#endif + +const u8* bes2600_chrdev_get_dpd_data(u32 *len) +{ +#ifdef BES2600_WRITE_DPD_TO_FILE + if(!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) { + /* read dpd data from file that stores factory dpd calibration data */ + if((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH, + &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) && + (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH, + &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) { + bes2600_err(BES2600_DBG_CHARDEV, "%s read dpd data fail\n", __func__); + return NULL; + } else { + bes2600_cdev.dpd_calied = true; + } + } +#endif + + if(!bes2600_cdev.dpd_calied) + return NULL; + if(len) + *len = bes2600_cdev.dpd_len; + + return bes2600_cdev.dpd_data; +} + +u8* bes2600_chrdev_get_dpd_buffer(u32 size) +{ + if(bes2600_cdev.dpd_data) + kfree(bes2600_cdev.dpd_data); + + bes2600_cdev.dpd_data = kmalloc(size, GFP_KERNEL); + if(!bes2600_cdev.dpd_data) { + return NULL; + } + + bes2600_cdev.dpd_len = DPD_BIN_SIZE; + + return bes2600_cdev.dpd_data; +} + +void bes2600_chrdev_free_dpd_data(void) +{ + if(bes2600_cdev.dpd_data) + kfree(bes2600_cdev.dpd_data); + + bes2600_cdev.dpd_data = NULL; + bes2600_cdev.dpd_len = 0; +} + +int bes2600_chrdev_update_dpd_data(void) +{ + u32 cal_crc = 0; + u32 dpd_crc = *((u32 *)bes2600_cdev.dpd_data); + + /* check if the dpd data is valid */ + cal_crc ^= 0xffffffffL; + cal_crc = crc32_le(cal_crc, bes2600_cdev.dpd_data + 4, bes2600_cdev.dpd_len - 4); + cal_crc ^= 0xffffffffL; + if(cal_crc != dpd_crc) { + bes2600_err(BES2600_DBG_CHARDEV, + "bes2600 dpd data check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n", + cal_crc, dpd_crc); + return -1; + } + + bes2600_info(BES2600_DBG_CHARDEV, "bes2600 dpd cali pass.\n"); + + /* update dpd calibration and wait state */ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.dpd_calied = true; + if(bes2600_chrdev_check_system_close_internal()) { + bes2600_cdev.wait_state = BES2600_BOOT_WAIT_CLOSE; + } else { + bes2600_cdev.wait_state = BES2600_BOOT_WAIT_PROBE_DONE; + } + spin_unlock(&bes2600_cdev.status_lock); + +#ifdef BES2600_WRITE_DPD_TO_FILE + /* write dpd data to file */ + memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE); + bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH, + bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE); +#endif + + + return 0; +} + +#ifdef BES2600_DUMP_FW_DPD_LOG +void bes2600_free_dpd_log_buffer(void) +{ + if (bes2600_cdev.dpd_log) + kfree(bes2600_cdev.dpd_log); + + bes2600_cdev.dpd_log_len = 0; + bes2600_cdev.dpd_log = NULL; +} + +u8* bes2600_alloc_dpd_log_buffer(u16 len) +{ + bes2600_cdev.dpd_log_len = len; + + if (bes2600_cdev.dpd_log) + kfree(bes2600_cdev.dpd_log); + + bes2600_cdev.dpd_log = kzalloc(len, GFP_KERNEL); + if (!bes2600_cdev.dpd_log) { + bes2600_cdev.dpd_log_len = 0; + return NULL; + } + + return bes2600_cdev.dpd_log; +} + +void bes2600_get_dpd_log(char **data, size_t *len) +{ + if (!bes2600_cdev.dpd_log) { + *data = NULL; + *len = 0; + } else { + *data = bes2600_cdev.dpd_log; + *len = (size_t)bes2600_cdev.dpd_log_len; + } +} +#endif /* BES2600_DUMP_FW_DPD_LOG */ + +void bes2600_chrdev_set_sbus_priv_data(struct sbus_priv *priv, bool error) +{ + bes2600_cdev.sbus_priv = priv; + if(priv) { + if(bes2600_cdev.bton_pending) { + bes2600_info(BES2600_DBG_CHARDEV, "execute pending bt on operation.\n"); + bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true); + + bes2600_cdev.bton_pending = false; + } + + spin_lock(&bes2600_cdev.status_lock); + if(bes2600_cdev.wait_state == BES2600_BOOT_WAIT_PROBE_DONE) { + bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE; + } + spin_unlock(&bes2600_cdev.status_lock); + + bes2600_info(BES2600_DBG_CHARDEV, "wakup proc on wq of probe_done.\n"); + } else { + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE; + bes2600_cdev.bus_error = error; + if(bes2600_cdev.bus_error) { + bes2600_cdev.wifi_opened = false; + bes2600_cdev.bt_opened = false; + bes2600_cdev.bton_pending = false; + bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE; + } + spin_unlock(&bes2600_cdev.status_lock); + bes2600_info(BES2600_DBG_CHARDEV, "wakup proc on wq of disconnect_done.\n"); + } + + wake_up(&bes2600_cdev.probe_done_wq); +} + +struct sbus_priv * bes2600_chrdev_get_sbus_priv_data(void) +{ + return bes2600_cdev.sbus_priv; +} + +int bes2600_chrdev_check_system_close(void) +{ + bool sys_closed = false; + + spin_lock(&bes2600_cdev.status_lock); + sys_closed = bes2600_chrdev_check_system_close_internal(); + spin_unlock(&bes2600_cdev.status_lock); + + return sys_closed; +} + +int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv) +{ + int ret = 0; + long status = 0; + + if(!sbus_ops || !priv) { + bes2600_warn(BES2600_DBG_CHARDEV, "abort power down device.\n"); + return 0; + } + + bes2600_dbg(BES2600_DBG_CHARDEV, "power down bes2600.\n"); + + /* trigger system to execute disconnect function */ + ret = sbus_ops->power_switch(priv, 0); + + /* wait disconnect event */ + status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 3); + WARN_ON(status <= 0); + + + return ret; +} + +bool bes2600_chrdev_is_wifi_opened(void) +{ + bool wifi_opened = false; + + spin_lock(&bes2600_cdev.status_lock); + wifi_opened = bes2600_cdev.wifi_opened; + spin_unlock(&bes2600_cdev.status_lock); + + if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL) + return true; + else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_BT) + return false; + else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) + return wifi_opened; + + return false; +} + +bool bes2600_chrdev_is_bt_opened(void) +{ + bool bt_opened = false; + + spin_lock(&bes2600_cdev.status_lock); + bt_opened = bes2600_cdev.bt_opened; + spin_unlock(&bes2600_cdev.status_lock); + + if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL) + return false; + else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_BT) + return true; + else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) + return bt_opened; + + return false; +} + +void bes2600_chrdev_wakeup_bt(void) +{ + int ret = 0; + + if(bes2600_cdev.bt_opened && bes2600_cdev.sbus_priv) { + bes2600_info(BES2600_DBG_PM, "wakeup bt in resume flow\n"); + ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true); + + bes2600_err_with_cond(ret, BES2600_DBG_PM, "Wakeup BT fail in resume\n"); + } +} + +int bes2600_chrdev_get_fw_type(void) +{ + return bes2600_cdev.fw_type; +} + +bool bes2600_chrdev_is_signal_mode(void) +{ + return bes2600_cdev.sig_mode; +} + +bool bes2600_chrdev_is_bus_error(void) +{ + bool error = false; + + spin_lock(&bes2600_cdev.status_lock); + error = (bes2600_cdev.bus_error || bes2600_cdev.bus_probe != BES2600_BUS_PROBE_OK); + spin_unlock(&bes2600_cdev.status_lock); + + return error; +} + +void bes2600_chrdev_update_signal_mode(void) +{ + if(bes2600_cdev.fw_type >= BES2600_FW_TYPE_MAX_NUM) { + bes2600_cdev.fw_type = BES2600_FW_TYPE_WIFI_SIGNAL; + bes2600_warn(BES2600_DBG_CHARDEV, "unexpected fw type, switch to wifi signal mode\n"); + } + + if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) { + bes2600_cdev.sig_mode = true; + } else if ((bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL) + || (bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)) { + bes2600_cdev.sig_mode = false; + } +} + +static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work) +{ + char wifi_state[15]; + char bt_state[15]; + char fw_type[15]; + char *env[] = { wifi_state, bt_state, fw_type, NULL }; + int ret; + + if(bes2600_chrdev_is_wifi_opened()) { + bes2600_info(BES2600_DBG_CHARDEV, "system exeception, force wifi down\n"); + + /* halt device if needed */ + if(bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->halt_device) { + bes2600_cdev.sbus_ops->halt_device(bes2600_cdev.sbus_priv); + } + + /* unregister wifi */ + bes2600_switch_wifi(0); + + /* power down device if wifi is only opened */ + if(bes2600_chrdev_check_system_close()) { + bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, + bes2600_cdev.sbus_priv); + } + + /* notify userspace */ + snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened); + snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened); + snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type); + ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env); + bes2600_err_with_cond(ret != 0, BES2600_DBG_CHARDEV, "bes2600 notify userspace failed\n"); + } +} + +void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev) +{ + if(hw_priv == NULL) + return; + + if(bes2600_chrdev_is_wifi_opened() && + !work_pending(&bes2600_cdev.wifi_force_close_work)) { + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.bus_error = true; + bes2600_cdev.halt_dev = halt_dev; + spin_unlock(&bes2600_cdev.status_lock); + + bes2600_tx_loop_set_enable(hw_priv); + schedule_work(&bes2600_cdev.wifi_force_close_work); + } +} + +static void bes2600_probe_timeout_work(struct work_struct *work) +{ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.bus_probe = BES2600_BUS_PROBE_TIMEOUT; + bes2600_cdev.wifi_opened = false; + bes2600_cdev.bt_opened = false; + bes2600_cdev.bton_pending = false; + spin_unlock(&bes2600_cdev.status_lock); + + bes2600_info(BES2600_DBG_CHARDEV, "bus probe timeout\n"); + wake_up(&bes2600_cdev.probe_done_wq); +} + +void bes2600_chrdev_start_bus_probe(void) +{ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.bus_probe = BES2600_BUS_PROBE_START; + spin_unlock(&bes2600_cdev.status_lock); + + cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work); + schedule_delayed_work(&bes2600_cdev.probe_timeout_work, (HZ * 8) / 10); +} + +void bes2600_chrdev_bus_probe_notify(void) +{ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.bus_probe = BES2600_BUS_PROBE_OK; + spin_unlock(&bes2600_cdev.status_lock); + cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work); + + wake_up(&bes2600_cdev.probe_done_wq); +} + +void bes2600_chrdev_wifi_update_wakeup_reason(u32 val) +{ + spin_lock(&bes2600_cdev.status_lock); + bes2600_cdev.wakeup_state = val; + spin_unlock(&bes2600_cdev.status_lock); +} + +int bes2600_chrdev_init(struct sbus_ops *ops) +{ + int ret = 0; + + /* allocate devide id */ + ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev"); + if(ret < 0){ + bes2600_err(BES2600_DBG_CHARDEV, "bes2600 alloc device id fail\n"); + ret = -EFAULT; + goto fail; + } + + /* extract major and minor device id */ + bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id); + bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id); + + /* add char device and bind operation function */ + bes2600_cdev.cdev.owner = THIS_MODULE; + cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops); + ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1); + if(ret < 0){ + bes2600_err(BES2600_DBG_CHARDEV, "bes2600 char device add fail\n"); + ret = -EFAULT; + goto fail1; + } + + /* create class for creating device node */ + bes2600_cdev.class = class_create(THIS_MODULE, "bes2600_chrdev"); + if (IS_ERR(bes2600_cdev.class)){ + bes2600_err(BES2600_DBG_CHARDEV, "bes2600 char device add fail\n"); + ret = -EFAULT; + goto fail2; + } + + /* get char device pointer */ + bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600"); + if(IS_ERR(bes2600_cdev.device)){ + bes2600_err(BES2600_DBG_CHARDEV, "bes2600 char device create fail\n"); + ret = -EFAULT; + goto fail3; + } + + /* initialise global variable */ + atomic_set(&bes2600_cdev.num_proc, 0); + init_waitqueue_head(&bes2600_cdev.open_wq); + spin_lock_init(&bes2600_cdev.status_lock); + init_waitqueue_head(&bes2600_cdev.probe_done_wq); + INIT_WORK(&bes2600_cdev.wifi_force_close_work, bes2600_chrdev_wifi_force_close_work); + INIT_DELAYED_WORK(&bes2600_cdev.probe_timeout_work, bes2600_probe_timeout_work); +#ifdef CONFIG_BES2600_WIFI_BOOT_ON + bes2600_cdev.wifi_opened = true; +#else + bes2600_cdev.wifi_opened = false; +#endif +#ifdef CONFIG_BES2600_BT_BOOT_ON + bes2600_cdev.bt_opened = true; + bes2600_cdev.bton_pending = true; +#else + bes2600_cdev.bt_opened = false; + bes2600_cdev.bton_pending = false; +#endif + bes2600_cdev.dpd_calied = false; + bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE; + bes2600_cdev.sbus_ops = ops; + bes2600_cdev.bus_error = false; + bes2600_cdev.halt_dev = false; + bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX; + bes2600_info(BES2600_DBG_CHARDEV, "%s done\n", __func__); + + return 0; + +fail3: + class_destroy(bes2600_cdev.class); +fail2: + cdev_del(&bes2600_cdev.cdev); +fail1: + unregister_chrdev_region(bes2600_cdev.dev_id, 1); +fail: + return ret; +} + +void bes2600_chrdev_free(void) +{ +#ifdef BES2600_DUMP_FW_DPD_LOG + bes2600_free_dpd_log_buffer(); +#endif + bes2600_chrdev_free_dpd_data(); + cdev_del(&bes2600_cdev.cdev); + unregister_chrdev_region(bes2600_cdev.dev_id, 1); + device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id); + class_destroy(bes2600_cdev.class); + bes2600_info(BES2600_DBG_CHARDEV, "%s done\n", __func__); +} diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h new file mode 100644 index 000000000000..8c7257c087a4 --- /dev/null +++ b/drivers/staging/bes2600/bes_chardev.h @@ -0,0 +1,70 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __BES_CHARDEV_H__ +#define __BES_CHARDEV_H__ + +#define BES2600_FW_TYPE_WIFI_SIGNAL 0 +#define BES2600_FW_TYPE_WIFI_NO_SIGNAL 1 +#define BES2600_FW_TYPE_BT 2 +#define BES2600_FW_TYPE_MAX_NUM 3 + +#define DPD_BIN_SIZE 0x3AF8 +#define DPD_BIN_FILE_SIZE 0x4000 + +enum pend_read_op { + BES_CDEV_READ_WAKEUP_STATE = 0, + /* add new here */ + + BES_CDEV_READ_NUM_MAX, +}; + +/* dpd management */ +u8* bes2600_chrdev_get_dpd_buffer(u32 size); +int bes2600_chrdev_update_dpd_data(void); +const u8* bes2600_chrdev_get_dpd_data(u32 *len); +void bes2600_chrdev_free_dpd_data(void); + +/* get/set subs_priv instance from/to bes_chrdev module */ +void bes2600_chrdev_set_sbus_priv_data(struct sbus_priv *priv, bool error); +struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void); + +/* used to control device power down */ +int bes2600_chrdev_check_system_close(void); +int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); +void bes2600_chrdev_wakeup_bt(void); +void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev); + +/* get and set internal state */ +bool bes2600_chrdev_is_wifi_opened(void); +bool bes2600_chrdev_is_bt_opened(void); +int bes2600_chrdev_get_fw_type(void); +bool bes2600_chrdev_is_signal_mode(void); +void bes2600_chrdev_update_signal_mode(void); +bool bes2600_chrdev_is_bus_error(void); + +/* bus probe check */ +void bes2600_chrdev_start_bus_probe(void); +void bes2600_chrdev_bus_probe_notify(void); + +/* set wifi wakeup state */ +void bes2600_chrdev_wifi_update_wakeup_reason(u32 val); + +/* init and deinit module */ +int bes2600_chrdev_init(struct sbus_ops *ops); +void bes2600_chrdev_free(void); + +#ifdef BES2600_DUMP_FW_DPD_LOG +void bes2600_free_dpd_log_buffer(void); +u8* bes2600_alloc_dpd_log_buffer(u16 len); +void bes2600_get_dpd_log(char **data, size_t *len); +#endif + +#endif \ No newline at end of file diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c new file mode 100644 index 000000000000..67f361c0431f --- /dev/null +++ b/drivers/staging/bes2600/bes_fw.c @@ -0,0 +1,1511 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "bes_fw_common.h" +#include "bes2600.h" +#include "hwio.h" +#include "sbus.h" +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" +#include +#include "bes2600_factory.h" + +#if defined(FW_DOWNLOAD_BY_SDIO) +//#define BES_FW_BUILTIN +#ifdef BES_FW_BUILTIN +#include "bes_firmware.h" +#endif + +struct platform_fw_t { + struct delayed_work work_data; + struct sdio_func *func; + struct completion completion_data; + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; +}; + +static void bes_fw_irq_handler(void *priv) +{ + struct platform_fw_t *fw_data = (struct platform_fw_t *)priv; + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s\n", __func__); + complete(&fw_data->completion_data); +} + +//#define BES_SLAVE_RX_DOUBLE_CHECK +static int bes_slave_rx_ready(struct platform_fw_t *fw_data, u8* buf_cnt, + u16* buf_len, int timeout) +{ + int ret; + unsigned long start = jiffies; + + do { + ret = bes2600_reg_read(0x108, buf_cnt, 1); + if (!(ret || buf_cnt)) { + mdelay(50); + continue; + } else if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s,%d err=%d\n", __func__, __LINE__, ret); + } else { + ret = bes2600_reg_read_16(0x109, buf_len); + } + break; + } while(time_before(jiffies, start + timeout)); + + return ret; +} + +//#define BES_SLAVE_TX_DOUBLE_CHECK +//#define MISSED_INTERRUPT_WORKAROUND +static int bes_slave_tx_ready(struct platform_fw_t *fw_data, u16 *tx_len, int timeout) +{ + int ret, retry = 0; + + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s now=%lu\n", __func__, jiffies); + + msleep(2); + + ret = wait_for_completion_interruptible_timeout(&fw_data->completion_data, timeout); + if (ret > 0) { +#ifdef MISSED_INTERRUPT_WORKAROUND +test_read_tx: +#endif + do { + ret = bes2600_reg_read_16(0, tx_len); + if (!ret && (*tx_len)) + break; + else + bes2600_err(BES2600_DBG_DOWNLOAD,"%s,%d ret=%d tx_len=%x retry=%d\n", + __func__, __LINE__, ret, *tx_len, retry); + retry++; + } while(retry <= 5); + reinit_completion(&fw_data->completion_data); + + } else if(!ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s now=%lu delta=%d\n", __func__, jiffies, timeout); +#ifndef MISSED_INTERRUPT_WORKAROUND + ret = -110; +#else + goto test_read_tx; +#endif + } else { + // ret = -ERESTARTSYS, to be continued; + } + + return ret; +} + +int bes_host_slave_sync(struct bes2600_common *hw_priv) +{ + u8 val; + int ret; + + ret = bes2600_reg_read(BES_HOST_INT_REG_ID, &val, 1); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s,%d err=%d\n", __func__, __LINE__, ret); + return ret; + } + + val |= BES_HOST_INT; + ret = bes2600_reg_write(BES_HOST_INT_REG_ID, &val, 1); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s,%d err=%d\n", __func__, __LINE__, ret); + } + return ret; +} + +//#define DATA_DUMP_OBSERVE + +static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val) +{ + u8 frame_num = 0; + u8 buf_cnt = 0; + u16 tx_size = 0; + u16 rx_size = 0; + u32 length = 0; + u8 *short_buf; + int ret; + + struct fw_msg_hdr_t header; + struct fw_info_t fw_info; + struct download_fw_t download_addr; + + fw_info.addr = addr; + fw_info.len = 4; + + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave rx buf cnt:%d,buf len max:%d\n", buf_cnt, tx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait bes sdio slave rx ready tiemout:%d\n", ret); + return ret; + } + + short_buf = kzalloc(512, GFP_KERNEL); + if (!short_buf) + return -ENOMEM; + + header.type = FRAME_HEADER_DOWNLOAD_INFO; + header.seq = frame_num; + header.len = sizeof(struct fw_info_t); + frame_num++; + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret); + goto err; + } + + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave tx ready %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process failed:%d\n", ret); + goto err; + } + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx download firmware info rsp err:%d\n", ret); + goto err; + } + + header.type = FRAME_HEADER_DOWNLOAD_DATA; + header.seq = frame_num; + header.len = 8; + frame_num++; + + download_addr.addr = fw_info.addr; + + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), &val, 4); + length = BES_FW_MSG_TOTAL_LEN(header); + + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret); + goto err; + } + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret); + goto err; + } + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx tx download fw data rsp err:%d\n", ret); + goto err; + } + +err: + kfree(short_buf); + return ret; +} + +static int bes_firmware_download_write_mem(struct platform_fw_t *fw_data, const u32 addr, const u8 *data, const u32 len) +{ + u8 frame_num = 0; + u8 last_frame_num = 0; + u8 buf_cnt = 0; + + u16 tx_size = 0; + u16 rx_size = 0; + + u32 length = 0; + u32 code_length = len; + u32 retry_cnt = 0; + int ret; + + const u8 *data_p; + u8 *short_buf, *long_buf; + + struct fw_msg_hdr_t header; + struct fw_info_t fw_info; + struct download_fw_t download_addr; + struct fw_crc_t crc32_t; + +retry: + fw_info.addr = addr; + fw_info.len = len; + data_p = data; + + crc32_t.crc32 = 0; + crc32_t.crc32 ^= 0xffffffffL; + crc32_t.crc32 = crc32_le(crc32_t.crc32, (u8 *)data, len); + crc32_t.crc32 ^= 0xffffffffL; + + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave rx buf cnt:%d,buf len max:%d\n", buf_cnt, tx_size); + } else { + bes2600_info(BES2600_DBG_DOWNLOAD, "wait bes sdio slave rx ready tiemout:%d\n", ret); + return ret; + } + + header.type = FRAME_HEADER_DOWNLOAD_INFO; + header.seq = frame_num; + header.len = sizeof(struct fw_info_t); + last_frame_num = frame_num; + frame_num++; + + short_buf = kzalloc(512, GFP_KERNEL); + if (!short_buf) + return -ENOMEM; + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + if (tx_size > length) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware info\n"); + } else { + bes2600_info(BES2600_DBG_DOWNLOAD, "%s:%d bes slave has no enough buffer%d/%d\n", __func__, __LINE__, tx_size, length); + goto err1; + } + + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret); + goto err1; + } + + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave tx ready %d bytes\n", rx_size); + } else { + bes2600_info(BES2600_DBG_DOWNLOAD, "wait slave process failed:%d\n", ret); + goto err1; + } + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx download firmware info rsp err:%d\n", ret); + goto err1; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware info err:%d\n", ret); + goto err1; + } + + //download firmware + long_buf = kmalloc(1024 * 32, GFP_KERNEL); + if (!long_buf) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d fw failed to allocate memory\n",__func__, __LINE__); + ret = -ENOMEM; + goto err1; + } + download_addr.addr = fw_info.addr; + + while (code_length) { + + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (ret) { + goto err2; + } else { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size); + } + + + if ((tx_size < 4) || (tx_size % 4)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d tx size=%d\n", __func__, __LINE__, tx_size); + ret = -203; + goto err2; + } + + if ((code_length + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)) < tx_size) { + length = code_length + sizeof(struct download_fw_t); + } else { + length = tx_size - sizeof(struct fw_msg_hdr_t); + } + + header.type = FRAME_HEADER_DOWNLOAD_DATA; + header.seq = frame_num; + header.len = length; + last_frame_num = frame_num; + frame_num++; + + memcpy(long_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(long_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t)); + length -= sizeof(struct download_fw_t);//real data length + memcpy(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), data_p, length); + + length += (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + + bes2600_dbg(BES2600_DBG_DOWNLOAD, "tx_download_firmware_data:%x %d\n", download_addr.addr, length); + + ret = bes2600_data_write(long_buf, length > 512 ? length : 512); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret); + goto err2; + } + length -= (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret); + goto err2; + } + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx tx download fw data rsp err:%d\n", ret); + goto err2; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp tx download fw err:%d\n", ret); + goto err2; + } + + code_length -= length; + data_p += length; + download_addr.addr += length; + bes2600_dbg(BES2600_DBG_DOWNLOAD, "already tx fw size:%x/%x\n", download_addr.addr - fw_info.addr, fw_info.len); + } + + //Notify Device:The firmware download is complete + + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (ret) { + goto err2; + } else { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size); + } + + header.type = FRAME_HEADER_DOWNLOAD_END; + header.seq = frame_num; + header.len = sizeof(struct fw_crc_t); + last_frame_num = frame_num; + frame_num++; + + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&crc32_t.crc32, sizeof(struct fw_crc_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware complete command\n"); + + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx downlod firmware complete command err:%d\n", ret); + goto err2; + } + + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret); + goto err2; + } + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "receive download firmware complete cmd rsp err:%d\n", ret); + goto err2; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware complete err:%d\n", ret); + goto err2; + } +err2: + kfree(long_buf); +err1: + kfree(short_buf); + + if (ret && retry_cnt < 3) { + retry_cnt++; + goto retry; + } + return ret; +} + +int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_name, bool auto_run) +{ + u8 frame_num = 0; + u8 last_frame_num = 0; + u8 buf_cnt = 0; + + u16 tx_size = 0; + u16 rx_size = 0; + + u32 length = 0; + u32 code_length = 0; + u32 retry_cnt = 0; + int ret; + const u8 *fw_ver_ptr; + const u8 *data_p; + u8 *short_buf, *long_buf; + +#ifndef BES_FW_BUILTIN +#ifdef CONFIG_FW_LOADER + const struct firmware *fw_bin; +#else + struct my_firmware_t { + u8 *data; + size_t size; + } my_fw, *fw_bin; + struct file *my_fwp; +#endif +#endif + +#ifdef DATA_DUMP_OBSERVE + char *observe; + size_t observe_len; + loff_t observe_off = 0; + mm_segment_t old_fs; + struct file *observe_file = NULL; +#endif + + struct fw_msg_hdr_t header; + struct fw_info_t fw_info; + struct download_fw_t download_addr; + struct fw_crc_t crc32_t; + struct run_fw_t run_addr; + +retry: +#ifndef BES_FW_BUILTIN +#ifdef CONFIG_FW_LOADER + ret = request_firmware(&fw_bin, fw_name, NULL); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "request firmware err:%d\n", ret); + return ret; + } +#else + my_fwp = filp_open(fw_name, O_RDONLY, 0); + if (IS_ERR(my_fwp)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "firmware path invalid:%s,%ld.\n", fw_name, PTR_ERR(my_fwp)); + return PTR_ERR(my_fwp); + } + fw_bin = &my_fw; + fw_bin->size = my_fwp->f_inode->i_size; + if (fw_bin->size <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wrong firmware size:%lu.\n", (long unsigned)fw_bin->size); + ret = -ENOENT; + goto close_fp; + } + fw_bin->data = kmalloc(fw_bin->size, GFP_KERNEL); + if (!fw_bin->data) { + bes2600_err(BES2600_DBG_DOWNLOAD, "kmalloc firmware buffer failed.\n"); + ret = -ENOMEM; + goto close_fp; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)) + ret = kernel_read(my_fwp, fw_bin->data, fw_bin->size, &my_fwp->f_pos); +#else + ret = kernel_read(my_fwp, my_fwp->f_pos, fw_bin->data, fw_bin->size); +#endif + if (ret != fw_bin->size) { + bes2600_err(BES2600_DBG_DOWNLOAD, "read firmware size error:%d,%lu.\n", ret, (long unsigned)fw_bin->size); + ret = -EIO; + goto free_mem; + } +#endif + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s fw.size=%ld\n", __func__, (long)fw_bin->size); +#endif + +#ifdef BES_FW_BUILTIN + bes_parse_fw_info((u8 *)firmware_device, FIRMWARE_SIZE , &fw_info.addr, &crc32_t.crc32); +#else + bes_parse_fw_info(fw_bin->data, fw_bin->size, &fw_info.addr, &crc32_t.crc32); +#endif + + fw_ver_ptr = bes2600_get_firmware_version_info(fw_bin->data, fw_bin->size); + if(fw_ver_ptr == NULL) + bes2600_err(BES2600_DBG_DOWNLOAD, "------Firmware version get failed\n"); + else + bes2600_info(BES2600_DBG_DOWNLOAD, "------Firmware: %s version :%s\n", fw_name ,fw_ver_ptr); + + bes2600_dbg(BES2600_DBG_DOWNLOAD, "------load addr :0x%08X\n", fw_info.addr); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "------data crc :0x%08X\n", crc32_t.crc32); + +#ifdef BES_FW_BUILTIN + code_length = FIRMWARE_SIZE - CODE_DATA_USELESS_SIZE; +#else + code_length = fw_bin->size - CODE_DATA_USELESS_SIZE; +#endif + bes2600_dbg(BES2600_DBG_DOWNLOAD, "------code size :%d\n", code_length); + + fw_info.len = code_length; +#ifdef BES_FW_BUILTIN + data_p = (u8 *)firmware_device; +#else + data_p = fw_bin->data; +#endif + + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave rx buf cnt:%d,buf len max:%d\n", buf_cnt, tx_size); + } else { + bes2600_info(BES2600_DBG_DOWNLOAD, "wait bes sdio slave rx ready tiemout:%d\n", ret); + return ret; + } + + header.type = FRAME_HEADER_DOWNLOAD_INFO; + header.seq = frame_num; + header.len = sizeof(struct fw_info_t); + last_frame_num = frame_num; + frame_num++; + + short_buf = kzalloc(512, GFP_KERNEL); + if (!short_buf) + return -ENOMEM; + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + //mdelay(5000); + bes2600_dbg_dump(BES2600_DBG_DOWNLOAD, "Fw Info:", short_buf, length); + + if (tx_size > length) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware info\n"); + } else { + bes2600_info(BES2600_DBG_DOWNLOAD, "%s:%d bes slave has no enough buffer%d/%d\n", __func__, __LINE__, tx_size, length); + goto err1; + } + + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret); + goto err1; + } + +#if 1 + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave tx ready %d bytes\n", rx_size); + } else { + bes2600_info(BES2600_DBG_DOWNLOAD, "wait slave process failed:%d\n", ret); + goto err1; + } +#ifdef BES_SLAVE_TX_DOUBLE_CHECK + if (rx_size != 8) + rx_size = 8; +#endif +#else + mdelay(100); + rx_size = 8; +#endif + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx download firmware info rsp err:%d\n", ret); + goto err1; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware info err:%d\n", ret); + goto err1; + } + + //download firmware + long_buf = kmalloc(1024 * 32, GFP_KERNEL); + if (!long_buf) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d fw failed to allocate memory\n",__func__, __LINE__); + ret = -ENOMEM; + goto err1; + } + download_addr.addr = fw_info.addr; + +#ifdef DATA_DUMP_OBSERVE + observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0); + if (IS_ERR(observe_file)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "create data_dump file err:%ld\n", IS_ERR(observe_file)); + observe_file = NULL; + } +#endif + + while (code_length) { + +#if 1 + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (ret) { + goto err2; + } else { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size); + } +#endif +#ifdef BES_SLAVE_RX_DOUBLE_CHECK + tx_size = 512; +#endif + + if ((tx_size < 4) || (tx_size % 4)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d tx size=%d\n", __func__, __LINE__, tx_size); + ret = -203; + goto err2; + } + + if ((code_length + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)) < tx_size) { + length = code_length + sizeof(struct download_fw_t); + } else { + length = tx_size - sizeof(struct fw_msg_hdr_t); + } + +#if 0 // for SDIO_USE_V2 + if (length + sizeof(struct fw_msg_hdr_t) > func->cur_blksize) { + length = (length + sizeof(struct fw_msg_hdr_t)) / func->cur_blksize * func->cur_blksize; + length -= sizeof(struct fw_msg_hdr_t); + } +#endif + + header.type = FRAME_HEADER_DOWNLOAD_DATA; + header.seq = frame_num; + header.len = length; + last_frame_num = frame_num; + frame_num++; + + memcpy(long_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(long_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t)); + length -= sizeof(struct download_fw_t);//real data length + memcpy(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), data_p, length); + + length += (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + + //mdelay(5000); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "tx_download_firmware_data:%x %d\n", download_addr.addr, length); + +#ifdef DATA_DUMP_OBSERVE + if (observe_file) { + observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t); + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(observe_file, observe, observe_len, &observe_off); + set_fs(old_fs); + } +#endif + + ret = bes2600_data_write(long_buf, length > 512 ? length : 512); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret); + goto err2; + } + length -= (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + +#if 1 + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret); + goto err2; + } +#ifdef BES_SLAVE_TX_DOUBLE_CHECK + if (rx_size != 8) + rx_size = 8; +#endif +#else + mdelay(100); + rx_size = 8; +#endif + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx tx download fw data rsp err:%d\n", ret); + goto err2; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp tx download fw err:%d\n", ret); + goto err2; + } + + code_length -= length; + data_p += length; + download_addr.addr += length; + bes2600_dbg(BES2600_DBG_DOWNLOAD, "already tx fw size:%x/%x\n", download_addr.addr - fw_info.addr, fw_info.len); + } + + //Notify Device:The firmware download is complete + +#if 1 + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (ret) { + goto err2; + } else { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size); + } +#endif +#ifdef BES_SLAVE_RX_DOUBLE_CHECK + tx_size = 512; +#endif + + header.type = FRAME_HEADER_DOWNLOAD_END; + header.seq = frame_num; + header.len = sizeof(struct fw_crc_t); + last_frame_num = frame_num; + frame_num++; + + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&crc32_t.crc32, sizeof(struct fw_crc_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware complete command\n"); + + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx downlod firmware complete command err:%d\n", ret); + goto err2; + } + +#if 1 + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret); + goto err2; + } +#ifdef BES_SLAVE_TX_DOUBLE_CHECK + if (rx_size != 8) + rx_size = 8; +#endif +#else + mdelay(100); + rx_size = 8; + bes2600_info(BES2600_DBG_DOWNLOAD, "enter sdio irqs:%d", enter_sdio_irqs); +#endif + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "receive download firmware complete cmd rsp err:%d\n", ret); + goto err2; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware complete err:%d\n", ret); + goto err2; + } + + if (auto_run == false) { + bes2600_info(BES2600_DBG_DOWNLOAD, "partial firmware(%s) is downloaded successfully\n", fw_name); + goto err2; + } + +#if 1 + ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ); + if (ret) { + goto err2; + } else { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size); + } +#endif +#ifdef BES_SLAVE_RX_DOUBLE_CHECK + tx_size = 512; +#endif + + //Notify Device:Run firmware + run_addr.addr = fw_info.addr; + + header.type = FRAME_HEADER_RUN_CODE; + header.seq = frame_num; + header.len = sizeof(struct run_fw_t); + last_frame_num = frame_num; + frame_num++; + + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&run_addr.addr, sizeof(struct run_fw_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + bes2600_dbg(BES2600_DBG_DOWNLOAD, "tx run firmware command:0x%X\n", run_addr.addr); + + length = length > 512 ? length : 512; + ret = bes2600_data_write(short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx run firmware command err:%d\n", ret); + goto err2; + } + +#if 1 + ret = bes_slave_tx_ready(fw_data, &rx_size, HZ); + if (!ret) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process run fw cmd err:%d\n", ret); + goto err2; + } +#ifdef BES_SLAVE_TX_DOUBLE_CHECK + if (rx_size != 8) + rx_size = 8; +#endif +#else + mdelay(100); + rx_size = 8; +#endif + + ret = bes2600_data_read(short_buf, rx_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rx run firmware command err:%d\n", ret); + goto err2; + } + + //check device rx status + ret = bes_frame_rsp_check(short_buf, last_frame_num); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp run firmware command err:%d\n", ret); + goto err2; + } + + bes2600_info(BES2600_DBG_DOWNLOAD, "%s", "firmware is downloaded successfully and is already running\n"); + msleep(500); + +err2: + kfree(long_buf); +#ifdef DATA_DUMP_OBSERVE + if (observe_file) { + filp_close(observe_file, NULL); + } +#endif +err1: + kfree(short_buf); +#ifndef BES_FW_BUILTIN +#ifdef CONFIG_FW_LOADER + release_firmware(fw_bin); +#else +free_mem: + kfree(fw_bin->data); +close_fp: + filp_close(my_fwp, NULL); +#endif +#endif + if (ret && retry_cnt < 3) { + retry_cnt++; + goto retry; + } + return ret; +} + +static int bes_read_dpd_data(struct platform_fw_t *fw_data) +{ + u16 dpd_size = 0; + int ret = 0; + u8 *dpd_buf = NULL; + u8 mcu_status = 0; + unsigned long wait_timeout; + + /* wait for device ready */ + wait_timeout = jiffies + 15 * HZ; + do { + msleep(100); + ret = bes2600_reg_read(BES_SLAVE_STATUS_REG_ID, &mcu_status, 1); + } while(((ret == 0) || (ret == -84)) && + !(mcu_status & BES_SLAVE_STATUS_DPD_READY) && + time_before(jiffies, wait_timeout)); + + /* check if read dpd error */ + if(ret < 0 || time_after(jiffies, wait_timeout)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd data ready failed:%d\n", ret); + return -1; + } + + /* wait dpd read ready */ + ret = bes_slave_tx_ready(fw_data, &dpd_size, HZ); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd data failed:%d\n", ret); + return -1; + } + + /* dpd size check */ + if (dpd_size != DPD_BIN_SIZE) { + bes2600_err(BES2600_DBG_DOWNLOAD, "get dpd data size err:%u\n", dpd_size); + return -1; + } + + /* read dpd data */ + dpd_buf = bes2600_chrdev_get_dpd_buffer(DPD_BIN_FILE_SIZE); + if(!dpd_buf) { + bes2600_err(BES2600_DBG_DOWNLOAD, "allocate dpd buffer failed.\n"); + return -1; + } + + ret = bes2600_data_read(dpd_buf, dpd_size); + bes2600_info(BES2600_DBG_DOWNLOAD, "read dpd data size:%d\n", dpd_size); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "read dpd data failed:%d\n", ret); + bes2600_chrdev_free_dpd_data(); + return -1; + } + + /* update dpd data */ + ret = bes2600_chrdev_update_dpd_data(); + + return ret; +} + +#ifdef BES2600_DUMP_FW_DPD_LOG +static int bes_read_dpd_log(struct platform_fw_t *fw_data) +{ + u16 dpd_log_size = 0; + int ret = 0; + u8 mcu_status = 0; + u8 *dpd_log = NULL; + unsigned long wait_timeout; + + /* wait for device ready */ + wait_timeout = jiffies + 5 * HZ; + do { + msleep(10); + ret = bes2600_reg_read(BES_SLAVE_STATUS_REG_ID, &mcu_status, 1); + } while(((ret == 0) || (ret == -84)) && + !(mcu_status & BES_SLAVE_STATUS_DPD_LOG_READY) && + time_before(jiffies, wait_timeout)); + + if(ret < 0 || time_after(jiffies, wait_timeout)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd log ready failed:%d\n", ret); + return -1; + } + + /* wait dpd log dump data ready */ + ret = bes_slave_tx_ready(fw_data, &dpd_log_size, HZ); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd log failed:%d\n", ret); + return -1; + } + + dpd_log = bes2600_alloc_dpd_log_buffer((dpd_log_size + 3) & (~0x3)); + if(!dpd_log) { + bes2600_err(BES2600_DBG_DOWNLOAD, "dpd log buffer alloc fail"); + return -1; + } + + ret = bes2600_data_read(dpd_log, (dpd_log_size + 3) & (~0x3)); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "read dpd log failed:%d\n", ret); + bes2600_free_dpd_log_buffer(); + return -1; + } + + bes2600_info(BES2600_DBG_DOWNLOAD, "read dpd log size: %u\n", dpd_log_size); + + return ret; +} +#endif /* BES2600_DUMP_FW_DPD_LOG */ + +static int bes2600_load_wifi_firmware(struct platform_fw_t *fw_data) +{ + int ret = 0; + const char *fw_name_tbl[3]; + int fw_type = bes2600_chrdev_get_fw_type(); + + fw_name_tbl[0] = BES2600_LOAD_FW_NAME; + fw_name_tbl[1] = BES2600_LOAD_NOSIGNAL_FW_NAME; + fw_name_tbl[2] = BES2600_LOAD_BTRF_FW_NAME; + + bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 download cali and wifi signal firmware.\n"); + ret = bes_firmware_download(fw_data, BES2600_LOAD_BOOT_NAME, true); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download dpd cali firmware failed\n"); + + if (!ret) { + bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 read dpd cali data.\n"); + ret = bes_read_dpd_data(fw_data); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "read dpd data failed.\n"); + } + +#ifdef BES2600_DUMP_FW_DPD_LOG + if (!ret) { + bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 read dpd log data.\n"); + ret = bes_read_dpd_log(fw_data); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "dump dpd log failed.\n"); + } +#endif + + /* for wifi non-signal mode, download second firmware directly */ + if (!ret && bes2600_chrdev_check_system_close()) { + bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 device power down.\n"); + ret = bes2600_chrdev_do_system_close(fw_data->sbus_ops, fw_data->sbus_priv); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "device down fail.\n"); + } else if (!ret) { + ret = bes_firmware_download(fw_data, fw_name_tbl[fw_type], true); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download normal firmware failed.\n"); + } + + return ret; +} + +static int bes2600_load_wifi_firmware_with_dpd(struct platform_fw_t *fw_data) +{ + int ret = 0; + u32 dpd_data_len = 0; + const u8 *dpd_data = NULL; + const char *fw_name_tbl[3]; + int fw_type = bes2600_chrdev_get_fw_type(); + + fw_name_tbl[0] = BES2600_LOAD_FW_NAME; + fw_name_tbl[1] = BES2600_LOAD_NOSIGNAL_FW_NAME; + fw_name_tbl[2] = BES2600_LOAD_BTRF_FW_NAME; + + dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len); + BUG_ON(!dpd_data); + + bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 download firmware with dpd.\n"); + ret = bes_firmware_download_write_mem(fw_data, BES2600_DPD_ADDR, dpd_data, dpd_data_len); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download dpd data failed.\n"); + if (!ret) { + ret = bes_firmware_download(fw_data, fw_name_tbl[fw_type], true); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download firmware failed after dpd download.\n"); + } + + return ret; +} + +static int bes2600_load_bt_firmware(struct platform_fw_t *fw_data) +{ + int ret = 0; + + /* for bt mode, don't need to download dpd cali firmware*/ + bes2600_info(BES2600_DBG_DOWNLOAD, "download bt test firmware.\n"); + ret = bes_firmware_download(fw_data, BES2600_LOAD_BTRF_FW_NAME, true); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download normal firmware failed.\n"); + + return ret; + +} + +int bes2600_load_firmware_sdio(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret = 0; + struct platform_fw_t *temp_fw_data; + u32 dpd_data_len = 0; + const u8 *dpd_data = NULL; + int fw_type = bes2600_chrdev_get_fw_type(); +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + u8 *factory_data = NULL; + u8 *file_buffer = NULL; + u32 factory_data_len = 0; +#endif + + temp_fw_data = kzalloc(sizeof(struct platform_fw_t), GFP_KERNEL); + if (!temp_fw_data) + return -ENOMEM; + + init_completion(&temp_fw_data->completion_data); + temp_fw_data->sbus_ops = ops; + temp_fw_data->sbus_priv = priv; + + temp_fw_data->sbus_ops->irq_subscribe(temp_fw_data->sbus_priv, + (sbus_irq_handler)bes_fw_irq_handler, temp_fw_data); + + bes_firmware_download_write_reg(temp_fw_data, 0x40100000, 0x802006); + bes_firmware_download_write_reg(temp_fw_data, 0x4008602C, 0x3E00C000); + +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -ENOMEM; + + bes2600_factory_lock(); +#ifdef FACTORY_SAVE_MULTI_PATH + if (!(factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH)) && + !(factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_DEFAULT_PATH))) { +#else + if (!(factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH))) { +#endif + bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n"); + } else { + bes2600_factory_data_check(factory_data); + factory_little_endian_cvrt(factory_data); + ret = bes_firmware_download_write_mem(temp_fw_data, BES2600_FACTORY_ADDR, factory_data, factory_data_len); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download factory data failed.\n"); + } + + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); +#endif + + bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw_type:%d.\n", __func__, fw_type); + if(fw_type == BES2600_FW_TYPE_BT) { + ret = bes2600_load_bt_firmware(temp_fw_data); + } else { + dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len); + if(dpd_data) { + ret = bes2600_load_wifi_firmware_with_dpd(temp_fw_data); + } else { + ret = bes2600_load_wifi_firmware(temp_fw_data); + } + } + + + /* don't register net device when wifi is closed */ + if(!ret && !bes2600_chrdev_is_wifi_opened()) { + ret = 1; + } + + + temp_fw_data->sbus_ops->irq_unsubscribe(temp_fw_data->sbus_priv); + kfree(temp_fw_data); + + bes2600_info(BES2600_DBG_DOWNLOAD, "download finished ,wifi_state:%d result is %d\n", bes2600_chrdev_is_wifi_opened(), ret); + + return ret; +} +#endif + +#if defined(BES2600_BOOT_UART_TO_SDIO) +static struct uart_dld_work_t { + struct work_struct dld_work; + struct completion completion_data; + void *priv; +} uart_dld_data; + +void bes2600_load_firmware_uart_work(struct work_struct *work) +{ + int ret; +#if defined(BES2600_LOAD_FW_TOOL_PATH) && defined(BES2600_LOAD_FW_TOOL_DEVICE) && defined(BES2600_LOAD_BOOT_PATCH_NAME) + char cmd_path[] = BES2600_LOAD_FW_TOOL_PATH; + char *cmd_argv[] = {cmd_path, BES2600_LOAD_FW_TOOL_DEVICE, BES2600_LOAD_BOOT_PATCH_NAME, NULL}; + char *cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin:/system/bin:/usr/bin", NULL}; +#else + #error "BES uart download should specify fimrware load tool" +#endif + complete(&uart_dld_data.completion_data); + if ((ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC))) { + bes2600_err(BES2600_DBG_DOWNLOAD, "call_usermodehelper failed:%d\n", ret); + } else { + msleep(200); + complete(&uart_dld_data.completion_data); + } +} + +int bes2600_boot_uart_to_sdio(struct sbus_ops *ops) +{ + int ret, retry = 0; + +uart_dld: + memset(&uart_dld_data, 0, sizeof(uart_dld_data)); + init_completion(&uart_dld_data.completion_data); + INIT_WORK(&uart_dld_data.dld_work, bes2600_load_firmware_uart_work); + schedule_work(&uart_dld_data.dld_work); + ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, HZ); + if (ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld boot patch start failed:%d\n", + __func__, ret); + return -110; + } + + if (ops->reset) + ops->reset(NULL); + + ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, 10 * HZ); + if (ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld boot patch end failed:%d(%d)\n", + __func__, ret, retry); + cancel_work_sync(&uart_dld_data.dld_work); + retry++; + if (retry < 10) + goto uart_dld; + else + return -110; + } else { + return 0; + } + +} +#endif + +#if defined(FW_DOWNLOAD_BY_UART) +static struct uart_dld_work_t { + struct work_struct dld_work; + struct completion completion_data; + char *fw_name; +} uart_dld_data; + +void bes2600_load_firmware_uart_work(struct work_struct *work) +{ + int ret; +#if defined(BES2600_LOAD_FW_TOOL_PATH) && defined(BES2600_LOAD_FW_TOOL_DEVICE) + char cmd_path[] = BES2600_LOAD_FW_TOOL_PATH; + char *cmd_argv[] = {cmd_path, BES2600_LOAD_FW_TOOL_DEVICE, uart_dld_data.fw_name, NULL}; + char *cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin:/system/bin:/usr/bin", NULL}; +#else + #error "BES uart download should specify fimrware load tool" +#endif + complete(&uart_dld_data.completion_data); + if ((ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC))) { + bes2600_err(BES2600_DBG_DOWNLOAD, "call_usermodehelper failed:%d\n", ret); + } else { + msleep(200); + complete(&uart_dld_data.completion_data); + } +} + +#ifdef FW_DOWNLOAD_UART_DAEMON +extern int bes2600_load_uevent(char *evn[]); +#endif + +static int bes2600_load_firmware_uart_wrapper(struct sbus_ops *ops, char *name) +{ + int ret = 0; +#ifdef FW_DOWNLOAD_UART_DAEMON + char driver[] = "DRIVER=bes2600_wlan"; + char vendor[100] = "VENDOR_DESC="; + char *env[] = {driver, vendor, NULL}; +#endif + memset(&uart_dld_data, 0, sizeof(uart_dld_data)); + init_completion(&uart_dld_data.completion_data); +#ifndef FW_DOWNLOAD_UART_DAEMON + uart_dld_data.fw_name = name; +#else + memcpy(vendor + strlen(vendor), name, strlen(name) + 1); +#endif + INIT_WORK(&uart_dld_data.dld_work, bes2600_load_firmware_uart_work); + +#ifndef FW_DOWNLOAD_UART_DAEMON + schedule_work(&uart_dld_data.dld_work); + ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, HZ); + if (ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld firmware start failed:%d\n", + __func__, ret); + return -110; + } + + if (ops->reset) { + ops->reset(NULL); + } + + ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, 10 * HZ); + if (ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld firmware end failed:%d\n", + __func__, ret); + return -110; + } else { + return 0; + } + +#else + + if (ops->reset) { + ops->reset(NULL); + } + + if ((ret = bes2600_load_uevent(env))) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld firmware start failed:%d\n", + __func__, ret); + } + return ret; +#endif +} + +static int bes_read_dpd_data(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret = 0, i; + u32 dpd_data_len, dpd_ready = 0; + u8 *dpd_data; + u32 *temp; + unsigned long dpd_started = jiffies; + + do { + bes2600_reg_read(BES_SLAVE_STATUS_REG_ID, &dpd_ready, 4); + if (dpd_ready & BES_SLAVE_STATUS_WIFI_CALI_READY) + break; + else + msleep(1000); + } while (time_before(jiffies, dpd_started + 10 * HZ)); + if (!(dpd_ready & BES_SLAVE_STATUS_WIFI_CALI_READY)) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait wifi cali timeout(%x)\n", dpd_ready); + return -110; + } + + if ( (ret = bes2600_reg_read(BES_TX_CTRL_REG_ID, &dpd_data_len, 4))) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s get dpd data len failed(%d)\n", __func__, dpd_data_len); + goto exit; + } + + dpd_data = bes2600_chrdev_get_dpd_buffer(dpd_data_len); + if (!dpd_data) { + ret = -ENOMEM; + goto exit; + } + + ops->lock(priv); + ret = ops->sbus_memcpy_fromio(priv, BES_CALI_DATA_ADDR, dpd_data, dpd_data_len); + ops->unlock(priv); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s get dpd data failed(%d)\n", __func__, ret); + goto free_dpd; + } else { + temp = (u32 *)dpd_data; + for (i = 0; i < (dpd_data_len >> 2); i++) { + temp[i] = swab32(temp[i]); + } + ret = bes2600_chrdev_update_dpd_data(); + } + return ret; +free_dpd: + bes2600_chrdev_free_dpd_data(); +exit: + return ret; +} + +static int bes_write_dpd_data(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret; + u32 dpd_data_len = 0, cfg = BES_DLD_DPD_DATA_DONE; + const u8 *dpd_data = NULL; + + dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len); + BUG_ON(!dpd_data); + + ops->lock(priv); + ret = ops->sbus_memcpy_toio(priv, BES_CALI_DATA_ADDR, dpd_data, dpd_data_len); + ops->unlock(priv); + if (ret) { + bes2600_err(BES2600_DBG_SPI, "%s rewrite dpd data failed:%d\n", __func__, ret); + return ret; + } else { + bes2600_err(BES2600_DBG_SPI, "%s rewrite dpd data success\n", __func__); + } + return bes2600_reg_write(BES_HOST_SUBINT_REG_ID, &cfg, 4); +} + +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX +static int bes_write_factory_data(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret = 0; + u32 factory_data_len = 0, cfg = BES_DLD_FACTORY_DATA_DONE; + u8 *factory_data = NULL; + u8 *file_buffer = NULL; + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -ENOMEM; + + bes2600_factory_lock(); + factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH); + if (!factory_data) { + bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n"); + } else { + bes2600_factory_data_check(factory_data); + factory_little_endian_cvrt(factory_data); + ops->lock(priv); + ret = ops->sbus_memcpy_toio(priv, BES_FACTORY_DATA_ADDR, factory_data, factory_data_len); + ops->unlock(priv); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "download factory data failed(%d)\n", ret); + return ret; + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "download factory data success:%x\n", *(u32 *)factory_data); + } + } + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + + return bes2600_reg_write(BES_HOST_SUBINT_REG_ID, &cfg, 4); +} +#endif /* CONFIG_BES2600_CALIB_FROM_LINUX */ + +static int bes_slave_sync(struct sbus_ops *ops, struct sbus_priv *priv) +{ + u32 sync_header; + u32 cfg = SPI_CONTINUOUS_CFG_VAL; + unsigned long sync_started = jiffies; + + do { + bes2600_reg_read(BES_HOST_SYNC_REG_ID, &sync_header, 4); + if (sync_header != BES_SLAVE_SYNC_HEADER) + msleep(100); + else + break; + } while (time_before(jiffies, sync_started + 5 * HZ)); + + if (sync_header != BES_SLAVE_SYNC_HEADER) { + bes2600_err(BES2600_DBG_DOWNLOAD, "spi slave sync failed(%x)\n", sync_header); + return -110; + } + + return bes2600_reg_write(SPI_RD_CFG_REG_ID, &cfg, 4); +} + +int bes2600_load_firmware_uart(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret; + u32 dpd_data_len = 0; + const u8 *dpd_data = NULL; + int fw_type = bes2600_chrdev_get_fw_type(); + char *fw_name; + + bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw_type:%d.\n", __func__, fw_type); + + if (fw_type == BES2600_FW_TYPE_BT) { + ret = bes2600_load_firmware_uart_wrapper(ops, BES2600_LOAD_BTRF_FW_NAME); + } else { +reload: + dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len); + if (!dpd_data) { + bes2600_info(BES2600_DBG_DOWNLOAD, "%s boot bin\n", __func__); + ret = bes2600_load_firmware_uart_wrapper(ops, BES2600_LOAD_BOOT_NAME); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s download boot failed:%d\n", __func__, ret); + goto exit; + } + + if (ops->init) + ops->init(priv, NULL); + + if ((ret = bes_slave_sync(ops, priv))) + goto exit; + + if ((ret = bes_read_dpd_data(ops, priv))) + goto exit; + + /* wifi nosignal: load best2002_fw_spi_nosignal.bin */ + if (fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL) + goto reload; + + /* normal: judge whether load best2002_fw_spi.bin or not*/ + if (!bes2600_chrdev_is_wifi_opened()) + ret = 1; + else + goto reload; + } else { + fw_name = (fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) ? BES2600_LOAD_FW_NAME : BES2600_LOAD_NOSIGNAL_FW_NAME; + if ((ret = bes2600_load_firmware_uart_wrapper(ops, fw_name))) { + bes2600_err(BES2600_DBG_SPI, "%s download normal fw failed:%d\n", __func__, ret); + goto exit; + } + + if ((ret = bes_slave_sync(ops, priv))) + goto exit; + +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + if ((ret = bes_write_factory_data(ops, priv))) + goto exit; +#endif + if ((ret = bes_write_dpd_data(ops, priv))) + goto exit; + } + } + +exit: + return ret; +} +#endif diff --git a/drivers/staging/bes2600/bes_fw_common.c b/drivers/staging/bes2600/bes_fw_common.c new file mode 100644 index 000000000000..5b79cdfe85f7 --- /dev/null +++ b/drivers/staging/bes2600/bes_fw_common.c @@ -0,0 +1,125 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "bes_fw_common.h" + +//#define BES_CRC32_DOUBLE_CHECK +#ifdef BES_CRC32_DOUBLE_CHECK +extern u32 bes_crc32_c(u32 crc, const u8 *data, u32 data_len); +#endif + +#if defined(FW_DOWNLOAD_BY_SDIO) || defined(FW_DOWNLOAD_BY_USB) +void bes_parse_fw_info(const u8 *data, u32 data_len, u32 *load_addr, u32 *crc32) +{ + u8 buffer[16]; + struct exec_struct_t exec_struct; + u32 exec_addr_last4byte; + u32 crc_le = 0; +#ifdef BES_CRC32_DOUBLE_CHECK + u32 crc_bes = 0; + u32 crc_be = 0; +#endif + crc_le = crc32_le(0xffffffffL, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE); + crc_le ^= 0xffffffffL; + +#ifdef BES_CRC32_DOUBLE_CHECK + crc_be = crc32_be(0xffffffffL, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE); + crc_be ^= 0xffffffffL; + crc_bes = bes_crc32_c(crc_bes, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE); +#endif + + //read entry,param,sp,exec_addr + memcpy((u8 *)buffer, (u8 *)data, sizeof(exec_struct)); + exec_struct.entry = ((struct exec_struct_t *)buffer)->entry;//PC + exec_struct.param = ((struct exec_struct_t *)buffer)->param; + exec_struct.sp = ((struct exec_struct_t *)buffer)->sp; + exec_struct.exec_addr = ((struct exec_struct_t *)buffer)->exec_addr;//load addr + + +#ifdef BES_CRC32_DOUBLE_CHECK + bes2600_dbg(BES2600_DBG_DOWNLOAD, "crc32 %x(le) %x(be) %x(bes)\n", crc_le, crc_be, crc_bes); +#else + bes2600_dbg(BES2600_DBG_DOWNLOAD, "crc32 :0x%08X\n", crc_le); +#endif + bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.entry :0x%08X\n", exec_struct.entry); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.param :0x%08X\n", exec_struct.param); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.sp :0x%08X\n", exec_struct.sp); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.exec_addr:0x%08X\n", exec_struct.exec_addr); + + exec_addr_last4byte = (*((u32 *)(data + data_len - 4))); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_addr_last4byte :0x%08X\n", exec_addr_last4byte); + if ((!exec_struct.exec_addr) || (exec_struct.exec_addr != exec_addr_last4byte && exec_addr_last4byte)) { + exec_struct.exec_addr = exec_addr_last4byte; + bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_addr_last4byte covered exec_struct.exec_addr\n"); + } + bes2600_dbg(BES2600_DBG_DOWNLOAD, "final exec_struct.exec_addr:0x%08X\n", exec_struct.exec_addr); + + *load_addr = exec_struct.exec_addr; + +#ifndef BES_CRC32_DOUBLE_CHECK + *crc32 = crc_le; +#else + *crc32 = crc_bes; +#endif +} + +int bes_frame_rsp_check(void *rsp, u8 frame_num) +{ + int ret = 0; + struct frame_struct_t *pframe = (struct frame_struct_t *)rsp; + if (pframe->type == FRAME_HEADER_REPLY) { + if (pframe->frame_num == frame_num) { + if (pframe->len == 4) { + if (pframe->payload == ERR_NONE) { + bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes slave download firmware is ready\n"); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "frame payload=0x%x\n", pframe->payload); + ret = -200; + } + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "payload len error:%u\n", pframe->len); + ret = -201; + } + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "frame num err. 0x%x != 0x%x. len:%u\n", + pframe->frame_num, frame_num, pframe->len); + ret = -202; + } + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "frame type err. type 0x%x num=0x%x(0x%x), len:%u\n", + pframe->type, pframe->frame_num, frame_num, pframe->len); + ret = -203; + } + return ret; +} + +const u8* bes2600_get_firmware_version_info(const u8 *data, u32 count) +{ + int i = 0; + const u8 *tmp_ptr = NULL; + const char month[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + if(!data || count < 4) + return NULL; + + for(tmp_ptr = data + count - 3; tmp_ptr > data; tmp_ptr -= 1) { + for(i = 0; i < 12; i++) { + if(memcmp(tmp_ptr, month[i], 3) == 0) { + return tmp_ptr; + } + } + } + + return NULL; +} +#endif \ No newline at end of file diff --git a/drivers/staging/bes2600/bes_fw_common.h b/drivers/staging/bes2600/bes_fw_common.h new file mode 100644 index 000000000000..05251aab410f --- /dev/null +++ b/drivers/staging/bes2600/bes_fw_common.h @@ -0,0 +1,103 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __BES_FW_COMMON_H__ +#define __BES_FW_COMMON_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bes2600.h" + +/***** firmware macro *****/ +#define BUF_SIZE 49152 +#define RETRY_CNT_MAX 3 +#define TIMEOUT_TIME 20 +#define FRAME_HEADER_SIZE 0x04 +#define CODE_DATA_USELESS_SIZE 0x04 +/****frame header code****/ +#define FRAME_HEADER_REPLY 0xB0 +#define FRAME_HEADER_DOWNLOAD_INFO 0xB1 +#define FRAME_HEADER_DOWNLOAD_DATA 0xB2 +#define FRAME_HEADER_DOWNLOAD_END 0xB3 +#define FRAME_HEADER_RUN_CODE 0xB4 + +/****frame length get****/ +#define BES_FW_MSG_TOTAL_LEN(msg) (sizeof(struct fw_msg_hdr_t) + ((struct fw_msg_hdr_t )(msg)).len) + +/****frame length get****/ +#define BES2600_DPD_ADDR 0x2008C000 +#define BES2600_FACTORY_ADDR 0x2008B000 + +/***** bes fw error code *****/ +enum ERR_CODE { + ERR_NONE = 0x00, + ERR_LEN = 0x01, +}; + +/***** data struct *****/ +struct frame_struct_t { + u8 type; + u8 frame_num; + u16 len; + u32 payload; +}; + +struct fw_msg_hdr_t { + u8 type; + u8 seq; + u16 len; +}; + +struct fw_msg_replay_t { + u32 replay; +}; + +struct fw_info_t { + u32 len; + u32 addr; +}; + +struct download_fw_t { + u32 addr; + u8 data[0]; +}; + +struct fw_crc_t { + u32 crc32; +}; + +struct run_fw_t { + u32 addr; +}; + +struct exec_struct_t { + u32 entry; + u32 param; + u32 sp; + u32 exec_addr; +}; + +#if defined(FW_DOWNLOAD_BY_SDIO) || defined(FW_DOWNLOAD_BY_USB) +void bes_parse_fw_info(const u8 *data, u32 data_len, u32 *load_addr, u32 *crc32); +int bes_frame_rsp_check(void *rsp, u8 frame_num); +const u8* bes2600_get_firmware_version_info(const u8 *data, u32 count); +#endif + +#endif \ No newline at end of file diff --git a/drivers/staging/bes2600/bes_fw_usb.c b/drivers/staging/bes2600/bes_fw_usb.c new file mode 100644 index 000000000000..1685bbc4ad24 --- /dev/null +++ b/drivers/staging/bes2600/bes_fw_usb.c @@ -0,0 +1,412 @@ +/* +* Mac80211 driver for BES2600 device +* +* Copyright (c) 2022, Bestechnic +* Author: +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ +#include "bes_fw_common.h" +#include "bes2600.h" +#include "hwio.h" +#include "sbus.h" +#include "bes2600_driver_mode.h" +#include +#include "bes_chardev.h" +struct platform_fw_t { + struct completion completion_tx; + struct completion completion_rx; + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; +}; + +static void bes_fw_irq_handler(void *priv) +{ + struct platform_fw_t *fw_data = (struct platform_fw_t *)priv; + u32 ctrl_reg, status_reg; + + // read status and control register + fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &ctrl_reg, 1); + fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_STATUS_REG, &status_reg, 1); + bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s ctrl_reg:0x%08x status_reg:0x%08x\n", __func__, ctrl_reg, status_reg); + + // notify tx done event + if((ctrl_reg & BES_USB_FW_TX_DONE) != 0 && + (status_reg & BES_USB_FW_TX_DONE) != 0) { + status_reg &= ~BES_USB_FW_TX_DONE; + fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_STATUS_REG, &status_reg, 1); + complete(&fw_data->completion_tx); + } + + // notify rx indication event + if((ctrl_reg & BES_USB_FW_RX_INDICATION) != 0 && + (status_reg & BES_USB_FW_RX_INDICATION) != 0) { + status_reg &= ~BES_USB_FW_RX_INDICATION; + fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_STATUS_REG, &status_reg, 1); + complete(&fw_data->completion_rx); + } +} + +static int bes_usb_fw_write(struct platform_fw_t *fw_data, u8* data, size_t len) +{ + size_t length = 0; + int ret = 0; + long time_left; + u32 control_reg = 0; + + // align data size, makes it suite for usb transfer + length = fw_data->sbus_ops->align_size(fw_data->sbus_priv, len); + + // enable tx done notification + fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + control_reg |= BES_USB_FW_TX_DONE; + fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + + // send firmware data to device + ret = fw_data->sbus_ops->pipe_send(fw_data->sbus_priv, BES2600_USB_PIPE_TX_WLAN, length, data); + if(ret < 0) { + fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + control_reg &= ~BES_USB_FW_TX_DONE; + fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + return ret; + } + + // wait for sending done of firmware data + time_left = wait_for_completion_interruptible_timeout(&fw_data->completion_tx, 5*HZ); + if(time_left == 0) + ret = -ETIMEDOUT; + else if(time_left < 0) + ret = time_left; + + // turn off tx done notification + fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + control_reg &= ~BES_USB_FW_TX_DONE; + fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + + return ret; +} + +static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_name, bool auto_run) +{ + u8 frame_num = 0; + u8 last_frame_num = 0; + + u16 tx_size = 8192; + + u32 length = 0; + u32 code_length = 0; + + int ret; + + const u8 *fw_ver_ptr; + const u8 *data_p; + u8 *short_buf, *long_buf; + const struct firmware *fw_bin; + struct sk_buff *skb = NULL; + + struct fw_msg_hdr_t header; + struct fw_info_t fw_info; + struct download_fw_t download_addr; + struct fw_crc_t crc32_t; + struct run_fw_t run_addr; + + // get firmware data + ret = request_firmware(&fw_bin, fw_name, NULL); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "request firmware err:%d\n", ret); + return ret; + } + + // parse firmware information + bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw.size=%ld\n", __func__, fw_bin->size); + bes_parse_fw_info(fw_bin->data, fw_bin->size, &fw_info.addr, &crc32_t.crc32); + bes2600_info(BES2600_DBG_DOWNLOAD, "------load addr :0x%08X\n", fw_info.addr); + bes2600_info(BES2600_DBG_DOWNLOAD, "------data crc :0x%08X\n", crc32_t.crc32); + code_length = fw_bin->size - CODE_DATA_USELESS_SIZE; + bes2600_info(BES2600_DBG_DOWNLOAD, "------code size :%d\n", code_length); + + fw_ver_ptr = bes2600_get_firmware_version_info(fw_bin->data, fw_bin->size); + if(fw_ver_ptr == NULL) + bes2600_err(BES2600_DBG_DOWNLOAD, "------Firmware version get failed\n"); + else + bes2600_info(BES2600_DBG_DOWNLOAD, "------Firmware: %s version :%s\n", fw_name ,fw_ver_ptr); + + fw_info.len = code_length; + data_p = fw_bin->data; + + // construct download information frame + header.type = FRAME_HEADER_DOWNLOAD_INFO; + header.seq = frame_num; + header.len = sizeof(struct fw_info_t); + last_frame_num = frame_num; + frame_num++; + + short_buf = kzalloc(512, GFP_KERNEL); + if (!short_buf) + return -ENOMEM; + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + if (tx_size > length) { + bes2600_info(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware info\n"); + } else { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d bes slave has no enough buffer%d/%d\n", __func__, __LINE__, tx_size, length); + goto err1; + } + + // send firmware information frame to device and wait tx done + ret = bes_usb_fw_write(fw_data, short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret); + goto err1; + } + + // wait rx indication + ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5); + if(ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "usb receive download firmware info response timeout or interrupt\n"); + ret = - ETIMEDOUT; + goto err1; + } + + // read device response + skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv); + WARN_ON(!skb); + + //check device rx status + ret = bes_frame_rsp_check(skb->data, last_frame_num); + dev_kfree_skb(skb); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware info err:%d\n", ret); + goto err1; + } + + // allocate memory for store download data frame + long_buf = kmalloc(1024 * 32, GFP_KERNEL); + if (!long_buf) { + bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d fw failed to allocate memory\n",__func__, __LINE__); + ret = -ENOMEM; + goto err1; + } + download_addr.addr = fw_info.addr; + + // download firmware data + while (code_length) { + if ((code_length + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)) < tx_size) { + length = code_length + sizeof(struct download_fw_t); + } else { + length = tx_size - sizeof(struct fw_msg_hdr_t); + } + + header.type = FRAME_HEADER_DOWNLOAD_DATA; + header.seq = frame_num; + header.len = length; + last_frame_num = frame_num; + frame_num++; + + memcpy(long_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(long_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t)); + length -= sizeof(struct download_fw_t);//real data length + memcpy(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), data_p, length); + + length += (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + + bes2600_info(BES2600_DBG_DOWNLOAD, "tx_download_firmware_data:%x %d\n", download_addr.addr, length); + + + ret = bes_usb_fw_write(fw_data, long_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret); + goto err2; + } + length -= (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); + + ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5); + if(ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "usb receive download data response timeout or interrupt\n"); + ret = - ETIMEDOUT; + goto err2; + } + + skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv); + WARN_ON(!skb); + + //check device rx status + ret = bes_frame_rsp_check(skb->data, last_frame_num); + dev_kfree_skb(skb); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp tx download fw err:%d\n", ret); + goto err2; + } + + code_length -= length; + data_p += length; + download_addr.addr += length; + bes2600_info(BES2600_DBG_DOWNLOAD, "already tx fw size:%x/%x\n", download_addr.addr - fw_info.addr, fw_info.len); + } + + //Notify Device:The firmware download is complete + // construct firmware download end frame + header.type = FRAME_HEADER_DOWNLOAD_END; + header.seq = frame_num; + header.len = sizeof(struct fw_crc_t); + last_frame_num = frame_num; + frame_num++; + + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&crc32_t.crc32, sizeof(struct fw_crc_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + bes2600_info(BES2600_DBG_DOWNLOAD, "tx download firmware complete command\n"); + + // send frimware download end frame to device + ret = bes_usb_fw_write(fw_data, short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx downlod firmware complete command err:%d\n", ret); + goto err2; + } + + // wait device response + ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5); + if (ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret); + ret = - ETIMEDOUT; + goto err2; + } + + // read response data + skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv); + WARN_ON(!skb); + + //check device rx status + ret = bes_frame_rsp_check(skb->data, last_frame_num); + dev_kfree_skb(skb); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware complete err:%d\n", ret); + goto err2; + } + + if (auto_run == false) { + bes2600_err(BES2600_DBG_DOWNLOAD, "partial firmware(%s) is downloaded successfully\n", fw_name); + goto err2; + } + + //Notify Device:Run firmware + // construct run code frame + run_addr.addr = fw_info.addr; + + header.type = FRAME_HEADER_RUN_CODE; + header.seq = frame_num; + header.len = sizeof(struct run_fw_t); + last_frame_num = frame_num; + frame_num++; + + memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t)); + memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&run_addr.addr, sizeof(struct run_fw_t)); + length = BES_FW_MSG_TOTAL_LEN(header); + + bes2600_info(BES2600_DBG_DOWNLOAD, "tx run firmware command:0x%X\n", run_addr.addr); + + // send run code frame to device + ret = bes_usb_fw_write(fw_data, short_buf, length); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "tx run firmware command err:%d\n", ret); + goto err2; + } + + // wait device response + ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5); + if (ret <= 0) { + bes2600_err(BES2600_DBG_DOWNLOAD, "wait run code err:%d\n", ret); + ret = - ETIMEDOUT; + goto err2; + } + + // read response data + skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv); + WARN_ON(!skb); + + //check device rx status + ret = bes_frame_rsp_check(skb->data, last_frame_num); + dev_kfree_skb(skb); + if (ret) { + bes2600_err(BES2600_DBG_DOWNLOAD, "rsp run firmware command err:%d\n", ret); + goto err2; + } + + bes2600_info(BES2600_DBG_DOWNLOAD, "firmware is downloaded successfully and is already running\n"); + +err2: + kfree(long_buf); + +err1: + kfree(short_buf); + release_firmware(fw_bin); + + return ret; +} + +static int bes2600_load_wifi_firmware(struct platform_fw_t *fw_data) +{ + int ret = 0; + const char *fw_name_tbl[3]; + int fw_type = bes2600_chrdev_get_fw_type(); + + fw_name_tbl[0] = BES2600_LOAD_FW_NAME; + fw_name_tbl[1] = BES2600_LOAD_NOSIGNAL_FW_NAME; + fw_name_tbl[2] = BES2600_LOAD_BTRF_FW_NAME; + + bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 download cali and wifi signal firmware.\n"); + ret = bes_firmware_download(fw_data, BES2600_LOAD_BOOT_NAME, true); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download dpd cali firmware failed\n"); + + if (!ret) { + //force wait for wifi dpd calib + msleep(5000); + ret = bes_firmware_download(fw_data, fw_name_tbl[fw_type], true); + } + return ret; +} + +int bes2600_load_firmware_usb(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret; + struct platform_fw_t *temp_fw_data; + u32 control_reg = 0; + int fw_type = bes2600_chrdev_get_fw_type(); + temp_fw_data = kzalloc(sizeof(struct platform_fw_t), GFP_KERNEL); + if (!temp_fw_data) + return -ENOMEM; + + init_completion(&temp_fw_data->completion_rx); + init_completion(&temp_fw_data->completion_tx); + temp_fw_data->sbus_ops = ops; + temp_fw_data->sbus_priv = priv; + + // enable rx indication + temp_fw_data->sbus_ops->sbus_reg_read(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + control_reg |= BES_USB_FW_RX_INDICATION; + temp_fw_data->sbus_ops->sbus_reg_write(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + + // subscribe irq handler + temp_fw_data->sbus_ops->irq_subscribe(temp_fw_data->sbus_priv, + (sbus_irq_handler)bes_fw_irq_handler, temp_fw_data); + + // download firmware + bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw_type:%d.\n", __func__, fw_type); + ret = bes2600_load_wifi_firmware(temp_fw_data); + // unsubscribe irq handler + temp_fw_data->sbus_ops->irq_unsubscribe(temp_fw_data->sbus_priv); + + // disable rx indication + temp_fw_data->sbus_ops->sbus_reg_read(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + control_reg &= ~BES_USB_FW_RX_INDICATION; + temp_fw_data->sbus_ops->sbus_reg_write(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1); + + kfree(temp_fw_data); + + return ret; +} \ No newline at end of file diff --git a/drivers/staging/bes2600/bes_nl80211_testmode_msg.h b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h new file mode 100644 index 000000000000..a05f9f862704 --- /dev/null +++ b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h @@ -0,0 +1,258 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES_NL80211_TESTMODE_MSG_H +#define BES_NL80211_TESTMODE_MSG_H + +/* example command structure for test purposes */ +struct bes_msg_test_t { + int dummy; +}; + +/* example reply structure for test purposes */ +struct bes_reply_test_t { + int dummy; +}; + +/* example event structure for test purposes */ +struct bes_event_test_t { + int dummy; +}; + +/* vendor to mcu cmd msg reply structure */ +struct vendor_rf_cmd_msg_reply { + u32 id; + u32 msg_len; + char ret_msg[1028]; +}; + +/* rf cmd msg reply assembly */ +void bes2600_rf_cmd_msg_assembly(u32 cmd_type, void *data, u32 msg_len); +/* do rf cmd msg */ +int bes2600_vendor_rf_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len); + +enum bes_msg_id { + BES_MSG_TEST = 0, /* for test purposes */ + BES_MSG_EVENT_TEST, /* for test purposes */ + BES_MSG_SET_SNAP_FRAME, + BES_MSG_EVENT_FRAME_DATA, +#ifdef CONFIG_BES2600_TESTMODE + BES_MSG_GET_TX_POWER_LEVEL, + BES_MSG_GET_TX_POWER_RANGE, + BES_MSG_SET_ADVANCE_SCAN_ELEMS, + BES_MSG_SET_TX_QUEUE_PARAMS, + BES_MSG_START_STOP_TSM, + BES_MSG_GET_TSM_PARAMS, + BES_MSG_GET_ROAM_DELAY, +#endif /*CONFIG_BES2600_TESTMODE*/ + BES_MSG_SET_POWER_SAVE, +#ifdef ROAM_OFFLOAD + BES_MSG_NEW_SCAN_RESULTS, +#endif /* ROAM_OFFLOAD */ + BES_MSG_ADD_IP_OFFLOAD, + BES_MSG_DEL_IP_OFFLOAD, + BES_MSG_SET_IP_OFFLOAD_PERIOD, + BES_MSG_VENDOR_RF_CMD, + BES_MSG_SAVE_CALI_TXT_TO_FLASH, + BES_MSG_EPTA_PARM_CONFIG, + BES_MSG_GET_KEEP_ALIVE_STREAM, + BES_MSG_MCU_CPUUSAGE, + /* Add new IDs here */ + + BES_MSG_ID_MAX, +}; + +enum vendor_rf_cmd_type { + VENDOR_RF_SIGNALING_CMD = 0, + VENDOR_RF_NOSIGNALING_CMD, + VENDOR_RF_GET_SAVE_FREQOFFSET_CMD, + VENDOR_RF_GET_SAVE_POWERLEVEL_CMD, + VENDOR_RF_SAVE_FREQOFFSET_CMD, + VENDOR_RF_SAVE_POWERLEVEL_CMD, + VENDOR_RF_POWER_CALIB_FINISH, + /* add new here */ + + VENDOR_RF_CMD_MAX, + + VENDOR_RF_SIG_NOSIG_MIX = 0xFFFFFFFF, +}; + +enum bes_nl80211_testmode_data_attributes { + BES_TM_MSG_ID = 0x0001, /* u32 type containing the BES message ID */ + BES_TM_MSG_DATA, /* message payload */ + + /* Max indicator so module test may add its own attributes */ + BES_TM_MSG_ATTR_MAX, +}; + +/** + * bes_msg_set_snap_frame - set SNAP frame format + * @len: length of SNAP frame, if 0 SNAP frame disabled + * @frame: SNAP frame format + * + * In this structure is difference between user space because + * format and length have to be hidden + * + */ +struct bes_msg_set_snap_frame { + u8 len; + u8 frame[0]; +}; + +struct vendor_epta_parm { + int wlan_duration; + int bt_duration; + int hw_epta_enable; +}; + +#ifdef CONFIG_BES2600_TESTMODE +/** + * bes_msg_set_txqueue_params - store Tx queue params + * @user_priority: User priority for which TSPEC negotiated + * @medium_time: Allowed medium time + * @expiry_time: The expiry time of MSDU + * + */ +struct bes_msg_set_txqueue_params { + u8 user_priority; + u16 medium_time; + u16 expiry_time; +}; + +/** + * bes_tsm_stats - To retrieve the Transmit Stream Measurement stats + * @actual_msrmt_start_time: The TSF at the time at which the measurement + * started + * @msrmt_duration: Duration for measurement + * @peer_sta_addr: Peer STA address + * @tid: TID for which measurements were made + * @reporting_reason: Reason for report sent + * @txed_msdu_count: The number of MSDUs transmitted for the specified TID + * @msdu_discarded_count: The number of discarded MSDUs for the specified TID + * @msdu_failed_count: The number of failed MSDUs for the specified TID + * @multi_retry_count: The number of MSDUs which were retried + * @qos_cfpolls_lost_count: The number of QOS CF polls frames lost + * @avg_q_delay: Average queue delay + * @avg_transmit_delay: Average transmit delay + * @bin0_range: Delay range of the first bin (Bin 0) + * @bin0: bin0 transmit delay histogram + * @bin1: bin1 transmit delay histogram + * @bin2: bin2 transmit delay histogram + * @bin3: bin3 transmit delay histogram + * @bin4: bin4 transmit delay histogram + * @bin5: bin5 transmit delay histogram + * + */ +struct bes_tsm_stats { + u64 actual_msrmt_start_time; + u16 msrmt_duration; + u8 peer_sta_addr[6]; + u8 tid; + u8 reporting_reason; + u32 txed_msdu_count; + u32 msdu_discarded_count; + u32 msdu_failed_count; + u32 multi_retry_count; + u32 qos_cfpolls_lost_count; + u32 avg_q_delay; + u32 avg_transmit_delay; + u8 bin0_range; + u32 bin0; + u32 bin1; + u32 bin2; + u32 bin3; + u32 bin4; + u32 bin5; +} __packed; + + +/** + * bes_msg_set_start_stop_tsm - To start or stop collecting TSM metrics in + * bes2600 driver + * @start: To start or stop collecting TSM metrics + * @up: up for which metrics to be collected + * @packetization_delay: Packetization period for this TID + * + */ +struct bes_msg_start_stop_tsm { + u8 start; /*1: To start, 0: To stop*/ + u8 up; + u16 packetization_delay; +}; + +/** + * power_save_elems - To enable/disable legacy power Save + */ +struct power_save_elems { + int powerSave; +}; + +#ifdef CONFIG_BES2600_KEEP_ALIVE +/* + * ip keep alive feature's parameters + * @idx add(idx=15) a tcp/udp keep alive stream, or the idx number(idx= 0-7) when reconfig one; + * set payload_len to 0 and payload to NULL when deleting one stream. + * @klv_vendor different number stands for different vendor's keepalive configuration + * @period: alive period + * @proto 0 for udp and 1 for tcp + * @src_port local port + * @dst_port tcp server's listen port + * @src_ip local ip address + * @dst_ip tcp server's ip address + * @key: key + * @iv: iv + * @payload payload of the keep alive packet + * @payload_len length of the payload + */ +struct ip_alive_paras { + uint16_t idx; + uint8_t klv_vendor; + uint8_t period; + uint8_t proto; + uint16_t src_port; + uint16_t dst_port; + uint32_t src_ip; + uint32_t dst_ip; + uint8_t key[16]; + uint8_t iv[16]; + uint16_t payload_len; + uint8_t payload[0]; +}; + +/** + * ip_alive_tac_idx - idx of tcp & udp alive stream + */ +struct ip_alive_iac_idx { + int idx; +}; + +/** + * ip_alive_period - tcp & udp alive period + */ +struct ip_alive_period { + int period; +}; + +/* + * err_code, 0: success, -1: cmd with idx err, + * -2: get stream fail, -3: playload err + */ +struct ip_stream_cfg { + int err_code; + int idx; +}; +#endif /* CONFIG_BES2600_KEEP_ALIVE */ + +#endif /* CONFIG_BES2600_TESTMODE */ + +#define BES_TM_MAX_ATTRIB_SIZE 1024 + +#endif /* BES_NL80211_TESTMODE_MSG_H*/ diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c new file mode 100644 index 000000000000..19d7ed26fde8 --- /dev/null +++ b/drivers/staging/bes2600/bes_pwr.c @@ -0,0 +1,1406 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bes2600.h" +#include "sbus.h" +#include "bes_pwr.h" +#include "sta.h" +#include "bes_chardev.h" + +static void bes2600_add_power_delay_event(struct bes2600_pwr_t *bes_pwr, u32 event, u32 timeout); + +static void bes2600_dump_power_busy_event(struct bes2600_pwr_t *bes_pwr, char* location) +{ + struct bes2600_pwr_event_t *item = NULL; + char *async_dump_str = NULL; + char *pending_dump_str = NULL; + char *free_dump_str = NULL; + int used_len = 0; + bool dump_async_ok = true; + bool dump_pending_ok = true; + bool dump_free_ok = true; + bool async_have = false; + bool pending_have = false; + bool free_have = false; + const int single_buffer_len = 4096; + const int all_bufer_len = single_buffer_len * 3; + + if(bes2600_get_dbg_lvl(BES2600_DBG_PWR) < BES2600_LOG_DBG) { + return; + } + + bes2600_dbg(BES2600_DBG_PWR, "power busy event dump at %s\n", location); + + async_dump_str = kzalloc(all_bufer_len, GFP_ATOMIC); + if(async_dump_str == NULL) { + bes2600_err(BES2600_DBG_PWR, "%s alloc buffer failed\n", __func__); + return; + } + + used_len = snprintf(async_dump_str, single_buffer_len, "async list["); + if(!list_empty(&bes_pwr->async_timeout_list)) { + list_for_each_entry(item, &bes_pwr->async_timeout_list, link) { + if(used_len + 10 < single_buffer_len) { + async_have = true; + used_len += snprintf(async_dump_str + used_len, 10, "%d(%d) ", + BES_PWR_EVENT_NUMBER(item->event), (item->event >> 31)); + } else { + dump_async_ok = false; + goto out; + } + } + } + if(used_len + 3 < single_buffer_len) { + used_len = async_have ? used_len - 1 : used_len; + used_len += sprintf(async_dump_str + used_len, "]\n"); + } else { + dump_async_ok = false; + goto out; + } + + pending_dump_str = async_dump_str + single_buffer_len; + used_len = snprintf(pending_dump_str, single_buffer_len, "pending list["); + if(!list_empty(&bes_pwr->pending_event_list)) { + list_for_each_entry(item, &bes_pwr->pending_event_list, link) { + if(used_len + 10 < single_buffer_len) { + pending_have = true; + used_len += snprintf(pending_dump_str + used_len, 10, "%d(%d) ", + BES_PWR_EVENT_NUMBER(item->event), (item->event >> 31)); + } else { + dump_pending_ok = false; + goto out; + } + } + } + if(used_len + 3 < single_buffer_len) { + used_len = pending_have ? used_len - 1 : used_len; + used_len += sprintf(pending_dump_str + used_len, "]\n"); + } else { + dump_pending_ok = false; + goto out; + } + + free_dump_str = pending_dump_str + single_buffer_len; + used_len = snprintf(free_dump_str, single_buffer_len, "free list["); + if(!list_empty(&bes_pwr->free_event_list)) { + list_for_each_entry(item, &bes_pwr->free_event_list, link) { + if(used_len + 10 < single_buffer_len) { + free_have = true; + used_len += snprintf(free_dump_str + used_len, 10, "%d(%d) ", + BES_PWR_EVENT_NUMBER(item->event), (item->event >> 31)); + } else { + dump_free_ok = false; + goto out; + } + } + } + if(used_len + 3 < single_buffer_len) { + used_len = free_have ? used_len - 1 : used_len; + used_len += sprintf(free_dump_str + used_len, "]\n"); + } else { + dump_free_ok = false; + goto out; + } +out: + if(!dump_async_ok) { + bes2600_err(BES2600_DBG_PWR, "buffer is not enough for dumping async event\n"); + } else { + bes2600_dbg(BES2600_DBG_PWR, "%s", async_dump_str); + } + + if(!dump_pending_ok) { + bes2600_err(BES2600_DBG_PWR, "buffer is not enough for dumping pending event\n"); + } else { + bes2600_dbg(BES2600_DBG_PWR, "%s", pending_dump_str); + } + + if(!dump_free_ok) { + bes2600_err(BES2600_DBG_PWR, "buffer is not enough for dumping free event\n"); + } else { + bes2600_dbg(BES2600_DBG_PWR, "%s", free_dump_str); + } + + kfree(async_dump_str); +} + +static char *bes2600_get_ps_mode_str(u8 mode) +{ + char *ps_mode_str = NULL; + + ps_mode_str = (mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : + mode == WSM_PSM_PS ? "WSM_PSM_PS" : + mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : + "UNKNOWN"); + + return ps_mode_str; +} + +static char *bes2600_get_mac_str(char *buffer, u32 ip) +{ + sprintf(buffer, "%d.%d.%d.%d", + (ip & 0xff),((ip >> 8) & 0xff), + ((ip >> 16) & 0xff), ((ip >> 24) & 0xff)); + + return buffer; +} + +static char *bes2600_get_pwr_busy_event_name(struct bes2600_pwr_event_t *item) +{ + char *name = NULL; + + switch(BES_PWR_EVENT_NUMBER(item->event)) { + case BES_PWR_LOCK_ON_SCAN: name = "SCAN"; break; + case BES_PWR_LOCK_ON_JOIN: name = "JOIN"; break; + case BES_PWR_LOCK_ON_TX: name = "TX"; break; + case BES_PWR_LOCK_ON_RX: name = "RX"; break; + case BES_PWR_LOCK_ON_FLUSH: name = "FLUSH"; break; + case BES_PWR_LOCK_ON_ROC: name = "ROC"; break; + case BES_PWR_LOCK_ON_WSM_TX: name = "WSM_TX"; break; + case BES_PWR_LOCK_ON_WSM_OPER: name = "WSM_OPER"; break; + case BES_PWR_LOCK_ON_BSS_LOST: name = "BSS_LOST"; break; + case BES_PWR_LOCK_ON_GET_IP: name = "GET_IP"; break; + case BES_PWR_LOCK_ON_PS_ACTIVE: name = "PS_ACTIVE"; break; + case BES_PWR_LOCK_ON_LMAC_RSP: name = "LMAC_RSP"; break; + case BES_PWR_LOCK_ON_AP: name = "AP"; break; + case BES_PWR_LOCK_ON_TEST_CMD: name = "TEST_CMD"; break; + case BES_PWR_LOCK_ON_MUL_REQ: name = "MUL_REQ"; break; + case BES_PWR_LOCK_ON_ADV_SCAN: name = "ADV_SCAN"; break; + case BES_PWR_LOCK_ON_DISCON: name = "DISCON"; break; + case BES_PWR_LOCK_ON_QUEUE_GC: name = "QUEUE_GC"; break; + case BES_PWR_LOCK_ON_AP_LP_BAD: name = "AP_LP_BAD"; break; + default: name = "UNKNOW"; break; + } + + return name; +} + +static unsigned long bes2600_get_pwr_busy_event_timeout(struct bes2600_pwr_event_t *item) +{ + unsigned long timeout = 0; + + if(BES_PWR_IS_CONSTANT_EVENT(item->event)) + return 0; + + if(time_after(jiffies, item->timeout)) + return 0; + + timeout = (item->timeout >= jiffies) ? + (item->timeout - jiffies) : (ULONG_MAX - jiffies + item->timeout); + + return timeout; +} + +static bool bes2600_update_power_delay_events(struct bes2600_pwr_t *bes_pwr, unsigned long *timeout) +{ + struct bes2600_pwr_event_t *item = NULL, *temp = NULL; + unsigned long max_timeout = 0; + bool constant_event_exist = false; + + /* move event from async_timeout_list to pending_event_list */ + if(!list_empty(&bes_pwr->async_timeout_list)) { + list_for_each_entry_safe(item, temp, &bes_pwr->async_timeout_list, link) { + bes2600_add_power_delay_event(bes_pwr, item->event, item->delay); + list_move_tail(&item->link, &bes_pwr->free_event_list); + } + } + + /* age power event and get max timeout */ + list_for_each_entry_safe(item, temp, &bes_pwr->pending_event_list, link) { + if(BES_PWR_IS_CONSTANT_EVENT(item->event)) { + constant_event_exist = true; + continue; + } + if(time_after(jiffies, item->timeout)) { + bes2600_dbg(BES2600_DBG_PWR, "power busy event:0x%08x timeout\n", item->event); + list_move_tail(&item->link, &bes_pwr->free_event_list); + } else { + if(max_timeout == 0) { + max_timeout = item->timeout; + } else { + max_timeout = time_after(item->timeout, max_timeout) + ? item->timeout : max_timeout; + } + } + } + + bes2600_dump_power_busy_event(bes_pwr, "refresh"); + + if(timeout) { + *timeout = max_timeout; + } + + return constant_event_exist; +} + +static void bes2600_add_async_timeout_power_delay_event(struct bes2600_pwr_t *bes_pwr, u32 event, u32 timeout) +{ + struct bes2600_pwr_event_t *item = NULL; + unsigned long max_timeout = 0; + bool match = false; + + /* check if the event is already in pending list */ + if (!list_empty(&bes_pwr->async_timeout_list)) { + list_for_each_entry(item, &bes_pwr->async_timeout_list, link) { + if(item->event == event) { + match = true; + break; + } + } + } + + /* update event information */ + if (match && (item != NULL)) { + item->delay = timeout; + } else { + /* delete expired event if free_event_list is empty */ + if(list_empty(&bes_pwr->free_event_list)) { + bes2600_info(BES2600_DBG_PWR, "%s, update delay event\n", __func__); + bes2600_update_power_delay_events(bes_pwr, &max_timeout); + } + + /* throw out exception if free_event_list is empty */ + BUG_ON(list_empty(&bes_pwr->free_event_list)); + + /* add event instance to pending list */ + bes2600_dbg(BES2600_DBG_PWR, "%s, add async event:%d(%d) timeout:%d\n", + __func__, BES_PWR_EVENT_NUMBER(event), event >> 31, timeout); + item = list_first_entry(&bes_pwr->free_event_list, + struct bes2600_pwr_event_t, link); + list_move_tail(&item->link, &bes_pwr->async_timeout_list); + item->event = event; + item->delay = timeout; + } +} + +static void bes2600_add_power_delay_event(struct bes2600_pwr_t *bes_pwr, u32 event, u32 timeout) +{ + struct bes2600_pwr_event_t *item = NULL; + unsigned long max_timeout = 0; + bool match = false; + + /* check if the event is already in pending list */ + if(!list_empty(&bes_pwr->pending_event_list)) { + list_for_each_entry(item, &bes_pwr->pending_event_list, link) { + if(item->event == event) { + match = true; + break; + } + } + } + + /* update event or add a new event */ + if(match && (item != NULL)) { + /* duplicate event */ + item->timeout = jiffies + (timeout * HZ + HZ * BES2600_POWER_DOWN_DELAY) / 1000; + bes2600_dbg(BES2600_DBG_PWR, "%s, update event:%d(%d) timeout:%d\n", + __func__, BES_PWR_EVENT_NUMBER(event), event >> 31, timeout); + } else { + /* delete expired event if free_event_list is empty */ + if(list_empty(&bes_pwr->free_event_list)) { + bes2600_info(BES2600_DBG_PWR, "%s, update delay event\n", __func__); + bes2600_update_power_delay_events(bes_pwr, &max_timeout); + } + + /* throw out exception if free_event_list is empty */ + BUG_ON(list_empty(&bes_pwr->free_event_list)); + + /* add event instance to pending list */ + bes2600_dbg(BES2600_DBG_PWR, "%s, add event:%d(%d) timeout:%d\n", + __func__, BES_PWR_EVENT_NUMBER(event), event >> 31, timeout); + item = list_first_entry(&bes_pwr->free_event_list, + struct bes2600_pwr_event_t, link); + list_move_tail(&item->link, &bes_pwr->pending_event_list); + item->event = event; + item->timeout = jiffies + (timeout * HZ + HZ * BES2600_POWER_DOWN_DELAY) / 1000; + bes2600_dump_power_busy_event(bes_pwr, "add new event"); + } +} + +static bool bes2600_del_pending_event(struct bes2600_pwr_t *bes_pwr, u32 event) +{ + struct bes2600_pwr_event_t *item = NULL, *temp = NULL; + bool matched = false; + + list_for_each_entry_safe(item, temp, &bes_pwr->pending_event_list, link) { + if(event == item->event) { + matched = true; + list_move_tail(&item->link, &bes_pwr->free_event_list); + bes2600_dbg(BES2600_DBG_PWR, "delete pending event:%d(%d)\n", + BES_PWR_EVENT_NUMBER(event), event >> 31); + break; + } + } + + bes2600_dump_power_busy_event(bes_pwr, "delete"); + + return matched; +} + +static void bes2600_flush_pending_events(struct bes2600_pwr_t *bes_pwr) +{ + struct bes2600_pwr_event_t *item = NULL, *temp = NULL; + + list_for_each_entry_safe(item, temp, &bes_pwr->pending_event_list, link) { + list_move_tail(&item->link, &bes_pwr->free_event_list); + bes2600_dbg(BES2600_DBG_PWR, "flush pending event:%d(%d)\n", + BES_PWR_EVENT_NUMBER(item->event), item->event >> 31); + } + bes2600_dump_power_busy_event(bes_pwr, "flush"); +} + +static void bes2600_trigger_power_delay_down(struct bes2600_pwr_t *bes_pwr, unsigned long max_timeout) +{ + /* get the gap of the two timestamp */ + max_timeout = (max_timeout >= jiffies) ? + (max_timeout - jiffies) : (ULONG_MAX - jiffies + max_timeout); + + bes2600_dbg(BES2600_DBG_PWR, "restart delayed work, timeout:%lu\n", max_timeout); + queue_delayed_work(bes_pwr->hw_priv->workqueue, &bes_pwr->power_down_work, max_timeout); +} + +static void bes2600_pwr_lock_tx(struct bes2600_common *hw_priv) +{ + unsigned long flags; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + if(!hw_priv->bes_power.pending_lock) { + hw_priv->bes_power.pending_lock = true; + bes2600_dbg(BES2600_DBG_PWR, "bes pwr lock tx\n"); + wsm_lock_tx_async(hw_priv); + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); +} + +static void bes2600_pwr_unlock_tx(struct bes2600_common *hw_priv) +{ + unsigned long flags; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + if(hw_priv->bes_power.pending_lock) { + hw_priv->bes_power.pending_lock = false; + bes2600_dbg(BES2600_DBG_PWR, "bes pwr unlock tx\n"); + wsm_unlock_tx(hw_priv); + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); +} + +static void bes2600_pwr_call_enter_lp_cb(struct bes2600_common *hw_priv) +{ + struct bes2600_pwr_enter_cb_item *item = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + if (!list_empty(&hw_priv->bes_power.enter_cb_list)) { + list_for_each_entry(item, &hw_priv->bes_power.enter_cb_list, link) { + if(item->cb != NULL) + item->cb(hw_priv); + } + } + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); +} + +static void bes2600_pwr_call_exit_lp_cb(struct bes2600_common *hw_priv) +{ + struct bes2600_pwr_exit_cb_item *item = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + if (!list_empty(&hw_priv->bes_power.exit_cb_list)) { + list_for_each_entry(item, &hw_priv->bes_power.exit_cb_list, link) { + if(item->cb != NULL) + item->cb(hw_priv); + } + } + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); +} + +static void bes2600_pwr_delete_all_cb(struct bes2600_common *hw_priv) +{ + struct bes2600_pwr_enter_cb_item *item = NULL, *temp = NULL; + struct bes2600_pwr_exit_cb_item *item1 = NULL, *temp1 = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + + /* delete all cb in enter_cb_list */ + list_for_each_entry_safe(item, temp, &hw_priv->bes_power.enter_cb_list, link) { + list_del(&item->link); + kfree(item); + } + + /* delete all cb in exit_cb_list */ + list_for_each_entry_safe(item1, temp1, &hw_priv->bes_power.exit_cb_list, link) { + list_del(&item->link); + kfree(item); + } + + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); +} + +static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv) +{ + int ret = 0; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + + bes2600_dbg(BES2600_DBG_PWR, "host unlock lmac\n"); + ret = wsm_set_operational_mode(hw_priv, &mode, 0); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__); + + /* wait other module to finish work */ + bes2600_pwr_call_enter_lp_cb(hw_priv); + + if(hw_priv->sbus_ops->sbus_deactive) { + ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_MCU); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, deactive mcu fail\n", __func__); + } + + if(hw_priv->sbus_ops->gpio_sleep) + hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); + bes2600_info(BES2600_DBG_PWR, "device enter sleep\n"); +} + +static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) +{ + int i = 0; + struct bes2600_vif *priv; + int ret = 0; + char ip_str[20]; + unsigned long status = 0; + + /* set interface low power configuration */ + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if (i == (CW12XX_MAX_VIFS - 1)) + continue; +#endif + if (!priv) + continue; + + if (priv->join_status == BES2600_JOIN_STATUS_STA && + priv->bss_params.aid && + priv->setbssparams_done && + priv->filter4.enable) { + /* enable arp filter */ + bes2600_dbg(BES2600_DBG_PWR, "%s, arp filter, enable:%d addr:%s\n", + __func__, priv->filter4.enable, bes2600_get_mac_str(ip_str, priv->filter4.ipv4Address[0])); + ret = wsm_set_arp_ipv4_filter(hw_priv, &priv->filter4, priv->if_id); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set arp filter failed\n", __func__); + + /* skip beacon receive if applications don't have muticast service */ + if(priv->join_dtim_period && !priv->has_multicast_subscription) { + unsigned listen_interval = 1; + if(priv->join_dtim_period >= CONFIG_BES2600_LISTEN_INTERVAL) { + listen_interval = priv->join_dtim_period; + } else { + listen_interval = CONFIG_BES2600_LISTEN_INTERVAL / priv->join_dtim_period; + } + ret = wsm_set_beacon_wakeup_period(hw_priv, 1, listen_interval, priv->if_id); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set wakeup period failed\n", __func__); + } + + /* Set Enable Broadcast Address Filter */ + priv->broadcast_filter.action_mode = WSM_FILTER_ACTION_FILTER_OUT; + if (priv->join_status == BES2600_JOIN_STATUS_AP) + priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_A3; + ret = bes2600_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set bc filter failed\n", __func__); + + /* enter low power mode */ + if(!hw_priv->bes_power.ap_lp_bad) { + bes2600_dbg(BES2600_DBG_PWR, "%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n", + __func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod, + priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod); + atomic_set(&hw_priv->bes_power.pm_set_in_process, 1); + ret = bes2600_set_pm(priv, &priv->powersave_mode); + if (ret) { + atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); + bes2600_err(BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__); + } + + /* wait power save mode changed indication */ + status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ); + atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); + reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); + bes2600_err_with_cond(!status, BES2600_DBG_PWR, "%s, wait pm ind timeout\n", __func__); + } else { + bes2600_info(BES2600_DBG_PWR, "skip enter lp mode\n"); + } + } + } + + /* set device low power configuration */ + bes2600_pwr_device_enter_lp_mode(hw_priv); + + return ret; +} + +static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv) +{ + int ret = 0; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_active, + .disableMoreFlagUsage = true, + }; + + bes2600_dbg(BES2600_DBG_PWR, "host lock lmac\n"); + if(hw_priv->sbus_ops->gpio_wake) + hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); + + if(hw_priv->sbus_ops->sbus_active) { + ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, active mcu fail\n", __func__); + } + + ret = wsm_set_operational_mode(hw_priv, &mode, 0); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__); + bes2600_info(BES2600_DBG_PWR, "device exit sleep\n"); +} + +static int bes2600_pwr_exit_lp_mode(struct bes2600_common *hw_priv) +{ + int i = 0, ret = 0; + struct bes2600_vif *priv; + struct wsm_arp_ipv4_filter filter; + struct wsm_set_pm pm; + char ip_str[20]; + + /* set device low power configuration */ + bes2600_pwr_device_exit_lp_mode(hw_priv); + + /* set interface low power configutation */ + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if (i == (CW12XX_MAX_VIFS - 1)) + continue; +#endif + if (!priv) + continue; + + if (priv->join_status == BES2600_JOIN_STATUS_STA && + priv->bss_params.aid && + priv->setbssparams_done && + priv->filter4.enable) { + /* enable arp filter */ + filter = priv->filter4; + filter.enable = false; + bes2600_dbg(BES2600_DBG_PWR, "%s, arp filter, enable:%d addr:%s\n", + __func__, filter.enable, bes2600_get_mac_str(ip_str, filter.ipv4Address[0])); + ret = wsm_set_arp_ipv4_filter(hw_priv, &filter, priv->if_id); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set arp filter failed\n", __func__); + + /* set wakeup perioid */ + wsm_set_beacon_wakeup_period(hw_priv, priv->join_dtim_period, 0, priv->if_id); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set wakeup period failed\n", __func__); + + /* Set Enable Broadcast Address Filter */ + priv->broadcast_filter.action_mode = WSM_FILTER_ACTION_IGNORE; + if (priv->join_status == BES2600_JOIN_STATUS_AP) + priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_NONE; + bes2600_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set bc filter failed\n", __func__); + + /* exit low power mode */ + if(!hw_priv->bes_power.ap_lp_bad) { + pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_ACTIVE; + bes2600_dbg(BES2600_DBG_PWR, "%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n", + __func__, bes2600_get_ps_mode_str(pm.pmMode), pm.fastPsmIdlePeriod, pm.apPsmChangePeriod, pm.minAutoPsPollPeriod); + ret = bes2600_set_pm(priv, &pm); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__); + } else { + bes2600_info(BES2600_DBG_PWR, "skip exit lp mode\n"); + } + } + } + + /* call all exit lower power callback */ + bes2600_pwr_call_exit_lp_cb(hw_priv); + + return ret; +} + +static void bes2600_pwr_unlock_device(struct bes2600_common *hw_priv) +{ + unsigned long flags; + + /* set device to low power mode */ + mutex_lock(&hw_priv->bes_power.pwr_mutex); + + bes2600_pwr_lock_tx(hw_priv); + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKED) { + hw_priv->bes_power.power_state = POWER_DOWN_STATE_UNLOCKING; + hw_priv->bes_power.power_down_task = current; + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + bes2600_pwr_enter_lp_mode(hw_priv); + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + hw_priv->bes_power.power_down_task = NULL; + hw_priv->bes_power.power_state = POWER_DOWN_STATE_UNLOCKED; + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + } else { + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + } + mutex_unlock(&hw_priv->bes_power.pwr_mutex); + + /* allow system to enter suspend mode */ + // pm_relax(hw_priv->pdev); +} + +static void bes2600_pwr_lock_device(struct bes2600_common *hw_priv) +{ + unsigned long flags; + + /* prevent system from entering suspend mode */ + // pm_stay_awake(hw_priv->pdev); + + /* wakeup device from low power mode */ + mutex_lock(&hw_priv->bes_power.pwr_mutex); + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) { + hw_priv->bes_power.power_state = POWER_DOWN_STATE_LOCKING; + hw_priv->bes_power.power_up_task = current; + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + bes2600_pwr_exit_lp_mode(hw_priv); + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + hw_priv->bes_power.power_up_task = NULL; + hw_priv->bes_power.power_state = POWER_DOWN_STATE_LOCKED; + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + } else { + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + } + + bes2600_pwr_unlock_tx(hw_priv); + + mutex_unlock(&hw_priv->bes_power.pwr_mutex); +} + +static void bes2600_pwr_trigger_delayed_work(struct bes2600_common *hw_priv) +{ + unsigned long max_timeout = 0; + bool constant_event_exist = false; + unsigned long flags; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + + if(!constant_event_exist && max_timeout > 0) { + bes2600_trigger_power_delay_down(&hw_priv->bes_power, max_timeout); + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); +} + +static void bes2600_power_down_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, bes_power.power_down_work.work); + + unsigned long max_timeout = 0; + bool constant_event_exist = false; + unsigned long flags; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + if(!constant_event_exist && max_timeout == 0) { + /* no pending event, unlock device */ + bes2600_dbg(BES2600_DBG_PWR, "%s no pending event\n", __func__); + bes2600_pwr_unlock_device(hw_priv); + wake_up(&hw_priv->bes_power.dev_lp_wq); + } else { + /* have power busy event, lock device */ + bes2600_dbg(BES2600_DBG_PWR, "%s have pending event\n", __func__); + bes2600_pwr_lock_device(hw_priv); + + /* only have delayed power busy event, restart delayed work */ + if(!constant_event_exist && max_timeout > 0) { + bes2600_dbg(BES2600_DBG_PWR, "%s restart delayed work\n", __func__); + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + bes2600_trigger_power_delay_down(&hw_priv->bes_power, max_timeout); + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + } + } +} + +static void bes2600_power_async_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, bes_power.power_async_work); + + bes2600_power_down_work(&hw_priv->bes_power.power_down_work.work); +} + +static void bes2600_power_mcu_down_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, bes_power.power_mcu_down_work); + unsigned long max_timeout = 0; + bool constant_event_exist = false; + unsigned long flags; + enum power_down_state power_state; + int ret = 0; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + power_state = hw_priv->bes_power.power_state; + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + if(!constant_event_exist && + max_timeout == 0 && + power_state == POWER_DOWN_STATE_UNLOCKED) { + bes2600_info(BES2600_DBG_PWR, "mcu sleep directly"); + mutex_lock(&hw_priv->bes_power.pwr_mutex); + + if(hw_priv->sbus_ops->gpio_wake) + hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); + + if(hw_priv->sbus_ops->sbus_active) { + ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, active mcu fail\n", __func__); + } + + if(hw_priv->sbus_ops->sbus_deactive) { + ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_MCU); + bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, deactive mcu fail\n", __func__); + } + + if(hw_priv->sbus_ops->gpio_sleep) + hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); + mutex_unlock(&hw_priv->bes_power.pwr_mutex); + } +} + +void bes2600_pwr_init(struct bes2600_common *hw_priv) +{ + int i = 0; + + hw_priv->bes_power.power_state = POWER_DOWN_STATE_UNLOCKED; + hw_priv->bes_power.hw_priv = hw_priv; + spin_lock_init(&hw_priv->bes_power.pwr_lock); + INIT_DELAYED_WORK(&hw_priv->bes_power.power_down_work, bes2600_power_down_work); + INIT_WORK(&hw_priv->bes_power.power_async_work, bes2600_power_async_work); + INIT_WORK(&hw_priv->bes_power.power_mcu_down_work, bes2600_power_mcu_down_work); + INIT_LIST_HEAD(&hw_priv->bes_power.async_timeout_list); + INIT_LIST_HEAD(&hw_priv->bes_power.pending_event_list); + INIT_LIST_HEAD(&hw_priv->bes_power.free_event_list); + mutex_init(&hw_priv->bes_power.pwr_cb_mutex); + INIT_LIST_HEAD(&hw_priv->bes_power.enter_cb_list); + INIT_LIST_HEAD(&hw_priv->bes_power.exit_cb_list); + hw_priv->bes_power.pending_lock = false; + hw_priv->bes_power.power_down_task = NULL; + hw_priv->bes_power.power_up_task = NULL; + mutex_init(&hw_priv->bes_power.pwr_mutex); + atomic_set(&hw_priv->bes_power.dev_state, 0); + init_completion(&hw_priv->bes_power.pm_enter_cmpl); + sema_init(&hw_priv->bes_power.sync_lock, 1); + device_set_wakeup_capable(hw_priv->pdev, true); + init_waitqueue_head(&hw_priv->bes_power.dev_lp_wq); + + for(i = 0; i < BES2600_DELAY_EVENT_NUM; i++) { + hw_priv->bes_power.pwr_events[i].idx = i; + list_add_tail(&hw_priv->bes_power.pwr_events[i].link, &hw_priv->bes_power.free_event_list); + } +} + +void bes2600_pwr_exit(struct bes2600_common *hw_priv) +{ + bes2600_pwr_delete_all_cb(hw_priv); +} + +void bes2600_pwr_prepare(struct bes2600_common *hw_priv) +{ + /* wait stop operation end */ + down(&hw_priv->bes_power.sync_lock); +} + + +void bes2600_pwr_complete(struct bes2600_common *hw_priv) +{ + /* notify stop operation end */ + up(&hw_priv->bes_power.sync_lock); +} + +void bes2600_pwr_start(struct bes2600_common *hw_priv) +{ + unsigned long flags; + + if (!bes2600_chrdev_is_signal_mode()) + return ; + + /* start power management state machine */ + atomic_set(&hw_priv->bes_power.dev_state, 1); + bes2600_dbg(BES2600_DBG_PWR, "start power management.\n"); + + /* enable device wakeup function */ + device_wakeup_enable(hw_priv->pdev); + + /* set power_state to busy and clear state */ + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + hw_priv->bes_power.power_state = POWER_DOWN_STATE_LOCKED; + hw_priv->bes_power.power_down_task = NULL; + hw_priv->bes_power.power_up_task = NULL; + hw_priv->bes_power.sys_suspend_task = NULL; + hw_priv->bes_power.sys_resume_task = NULL; + hw_priv->bes_power.pending_lock = false; + hw_priv->bes_power.ap_lp_bad = false; + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + /* set gpio and prevent device from entering sleep mode */ + if(hw_priv->sbus_ops->gpio_wake) + hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); + + /* start idle timer */ + queue_delayed_work(hw_priv->workqueue, + &hw_priv->bes_power.power_down_work, (HZ * BES2600_POWER_DOWN_DELAY) / 1000); +} + +void bes2600_pwr_stop(struct bes2600_common *hw_priv) +{ + unsigned long flags; + unsigned long max_timeout; + + if (!bes2600_chrdev_is_signal_mode()) + return ; + + /* stop power management state machine */ + atomic_set(&hw_priv->bes_power.dev_state, 0); + bes2600_info(BES2600_DBG_PWR, "stop power management.\n"); + + /* cancel pending work */ + cancel_delayed_work_sync(&hw_priv->bes_power.power_down_work); + flush_delayed_work(&hw_priv->bes_power.power_down_work); + cancel_work_sync(&hw_priv->bes_power.power_async_work); + flush_work(&hw_priv->bes_power.power_async_work); + cancel_work_sync(&hw_priv->bes_power.power_mcu_down_work); + flush_work(&hw_priv->bes_power.power_mcu_down_work); + + /* delete all pending event and clear state */ + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + bes2600_flush_pending_events(&hw_priv->bes_power); + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + /* allow mcu sleep */ + bes2600_power_down_work(&hw_priv->bes_power.power_down_work.work); + bes2600_warn_with_cond(hw_priv->bes_power.power_state != POWER_DOWN_STATE_UNLOCKED, + BES2600_DBG_PWR, "power state is not unlocked when stop.\n"); + + /* unlock tx if tx is locked */ + bes2600_pwr_unlock_tx(hw_priv); + + /* disable device wakeup function */ + device_wakeup_disable(hw_priv->pdev); +} + +bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event) +{ + unsigned long max_timeout = 0; + bool constant_event_exist = false; + unsigned long flags; + bool pending = false; + struct bes2600_pwr_event_t *item = NULL; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + + if(constant_event_exist) { + BES_PWR_EVENT_SET_CONSTANT(event); + if(!list_empty(&hw_priv->bes_power.pending_event_list)) { + list_for_each_entry(item, &hw_priv->bes_power.pending_event_list, link) { + if(item->event == event) { + pending = true; + break; + } + } + } + } + + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + return pending; +} + +int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event) +{ + int ret = 0; + bool need_lock = false; + bool need_wait = false; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return -1; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + + /* don't set busy event if the command is for unlocking device */ + if((event == BES_PWR_LOCK_ON_WSM_TX) + && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) { + if(hw_priv->bes_power.power_down_task == current) { + /* BES_PWR_LOCK_ON_WSM_TX is from power down work */ + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + return 0; + } + } + + /* don't set busy event also if the command is for locking device */ + if((event == BES_PWR_LOCK_ON_WSM_TX) + && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING)) { + if(hw_priv->bes_power.power_up_task == current) { + /* BES_PWR_LOCK_ON_WSM_TX is from power down work */ + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + return 0; + } + } + + /* don't set busy event if the command is for suspend/resume */ + if((event == BES_PWR_LOCK_ON_WSM_TX) + && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)) { + if(hw_priv->bes_power.sys_suspend_task == current || + hw_priv->bes_power.sys_resume_task == current) { + /* BES_PWR_LOCK_ON_WSM_TX is from suspend/resume work */ + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + return 0; + } + } + + /* set busy event to pending_event_list */ + BES_PWR_EVENT_SET_CONSTANT(event); + bes2600_add_power_delay_event(&hw_priv->bes_power, event, 0); + + /* execute lock device operation or wait lock operation finish */ + if((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) + || (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) { + need_lock = true; + } else if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING + && (hw_priv->bes_power.power_up_task != current)) { + need_wait = true; + } + + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + if(need_wait) { + /* lock device is doing, wait operation done */ + bes2600_info(BES2600_DBG_PWR, "%s wait lock device done, event:%d\n", __func__, BES_PWR_EVENT_NUMBER(event)); + mutex_lock(&hw_priv->bes_power.pwr_mutex); + mutex_unlock(&hw_priv->bes_power.pwr_mutex); + } + + if(need_lock) { + /* cancel delayed work */ + cancel_delayed_work_sync(&hw_priv->bes_power.power_down_work); + flush_delayed_work(&hw_priv->bes_power.power_down_work); + + bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, BES_PWR_EVENT_NUMBER(event)); + bes2600_pwr_lock_device(hw_priv); + } + + return ret; +} + +int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event) +{ + bool need_lock = false; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return -1; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + BES_PWR_EVENT_SET_CONSTANT(event); + bes2600_add_power_delay_event(&hw_priv->bes_power, event, 0); + if(((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) + || (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING))) { + need_lock = true; + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + if(need_lock && !work_pending(&hw_priv->bes_power.power_async_work)) { + bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, BES_PWR_EVENT_NUMBER(event)); + queue_work(hw_priv->workqueue, &hw_priv->bes_power.power_async_work); + } + + return 0; + +} + +int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout) +{ + int ret = 0; + bool need_lock = false; + bool need_wait = false; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return -1; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + + /* add delayed event to pending_event_list */ + bes2600_add_power_delay_event(&hw_priv->bes_power, event, timeout); + + /* execute lock device operation or wait lock operation finish */ + if((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) + || (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) { + need_lock = true; + } else if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING + && (hw_priv->bes_power.power_up_task != current)) { + need_wait = true; + } + + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + if(need_wait) { + /* lock device is doing, wait operation done */ + bes2600_info(BES2600_DBG_PWR, "%s wait lock device done, event:%d\n", __func__, event); + mutex_lock(&hw_priv->bes_power.pwr_mutex); + mutex_unlock(&hw_priv->bes_power.pwr_mutex); + } + + if(need_lock) { + /* cancel delayed work */ + bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, event); + cancel_delayed_work_sync(&hw_priv->bes_power.power_down_work); + flush_delayed_work(&hw_priv->bes_power.power_down_work); + + bes2600_pwr_lock_device(hw_priv); + } + + if(!delayed_work_pending(&hw_priv->bes_power.power_down_work)) { + bes2600_pwr_trigger_delayed_work(hw_priv); + } + + return ret; +} + +int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout) +{ + int ret = 0; + bool need_lock = false; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return -1; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + if ((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) || + (hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKED)) { + bes2600_add_power_delay_event(&hw_priv->bes_power, event, timeout); + if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) { + need_lock = true; + } + } else if ((hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING) || + (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) { + bes2600_add_async_timeout_power_delay_event(&hw_priv->bes_power, event, timeout); + if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING) { + need_lock = true; + } + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + if(need_lock && !work_pending(&hw_priv->bes_power.power_async_work)) { + bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, event); + queue_work(hw_priv->workqueue, &hw_priv->bes_power.power_async_work); + } + + return ret; +} + + +int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event) +{ + int ret = 0; + u32 constant_event = event; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return -1; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + + /* don't need to clear busy event if the command is for unlocking device */ + if((event == BES_PWR_LOCK_ON_WSM_TX) + && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) { + if(hw_priv->bes_power.power_down_task == current) { + /* BES_PWR_LOCK_ON_WSM_TX is from power down work */ + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + return 0; + } + } + + /* don't need also to clear busy event if the command is for locking device */ + if((event == BES_PWR_LOCK_ON_WSM_TX) + && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING)) { + if(hw_priv->bes_power.power_up_task == current) { + /* BES_PWR_LOCK_ON_WSM_TX is for powering up operation */ + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + return 0; + } + } + + /* don't set busy event if the command is for suspend/resume */ + if((event == BES_PWR_LOCK_ON_WSM_TX) + && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)) { + if(hw_priv->bes_power.sys_suspend_task == current || + hw_priv->bes_power.sys_resume_task == current) { + /* BES_PWR_LOCK_ON_WSM_TX is from suspend/resume work */ + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + return 0; + } + } + + /* change constant event to delay event */ + BES_PWR_EVENT_SET_CONSTANT(constant_event); + if(bes2600_del_pending_event(&hw_priv->bes_power, constant_event)) { + bes2600_add_power_delay_event(&hw_priv->bes_power, event, 0); + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + /* trigger power down delayed work */ + if(!delayed_work_pending(&hw_priv->bes_power.power_down_work)) { + bes2600_dbg(BES2600_DBG_PWR, "%s restart delayed work\n", __func__); + bes2600_pwr_trigger_delayed_work(hw_priv); + } + + return ret; +} + +void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) +{ + if((psmode & 0x01) != WSM_PSM_ACTIVE) { + bes2600_dbg(BES2600_DBG_PWR, "complete pm_enter_cmpl\n"); + complete(&hw_priv->bes_power.pm_enter_cmpl); + } +} + +bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv) +{ + bool idle = false; + unsigned long flags; + + if (!bes2600_chrdev_is_signal_mode()) + return false; + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + idle = (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED); + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + return idle; +} + +void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) +{ + struct bes2600_pwr_enter_cb_item *item = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + list_for_each_entry(item, &hw_priv->bes_power.enter_cb_list, link) { + if(item->cb == cb) { + bes2600_warn(BES2600_DBG_PWR, "the enter cb is already exist\n"); + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); + return; + } + } + + item = kzalloc(sizeof(struct bes2600_pwr_enter_cb_item), GFP_KERNEL); + if(item) { + item->cb = cb; + list_add_tail(&item->link, &hw_priv->bes_power.enter_cb_list); + } + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); + + bes2600_err_with_cond(item == NULL, BES2600_DBG_PWR, "register en_lp_cb fail.\n"); +} + +void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) +{ + struct bes2600_pwr_enter_cb_item *item = NULL, *temp = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + list_for_each_entry_safe(item, temp, &hw_priv->bes_power.enter_cb_list, link) { + if(cb == item->cb) { + list_del(&item->link); + kfree(item); + break; + } + } + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); +} + +void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) +{ + struct bes2600_pwr_exit_cb_item *item = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + list_for_each_entry(item, &hw_priv->bes_power.exit_cb_list, link) { + if(item->cb == cb) { + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); + bes2600_warn(BES2600_DBG_PWR, "the exit cb is already exist\n"); + return; + } + } + + item = kzalloc(sizeof(struct bes2600_pwr_exit_cb_item), GFP_KERNEL); + if(item) { + item->cb = cb; + list_add_tail(&item->link, &hw_priv->bes_power.exit_cb_list); + } + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); + + bes2600_err_with_cond(item == NULL, BES2600_DBG_PWR, "register en_lp_cb fail.\n"); +} + +void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) +{ + struct bes2600_pwr_exit_cb_item *item = NULL, *temp = NULL; + + mutex_lock(&hw_priv->bes_power.pwr_cb_mutex); + list_for_each_entry_safe(item, temp, &hw_priv->bes_power.exit_cb_list, link) { + if(cb == item->cb) { + list_del(&item->link); + kfree(item); + break; + } + } + mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex); +} + +void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv) +{ + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return; + } + + mutex_lock(&hw_priv->bes_power.pwr_mutex); + hw_priv->bes_power.sys_suspend_task = current; + bes2600_pwr_device_exit_lp_mode(hw_priv); + mutex_unlock(&hw_priv->bes_power.pwr_mutex); +} + +void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv) +{ + unsigned long max_timeout = 0; + bool constant_event_exist = false; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + mutex_lock(&hw_priv->bes_power.pwr_mutex); + if(!constant_event_exist && max_timeout == 0) + bes2600_pwr_device_enter_lp_mode(hw_priv); + hw_priv->bes_power.sys_suspend_task = NULL; + mutex_unlock(&hw_priv->bes_power.pwr_mutex); +} + +void bes2600_pwr_resume_start(struct bes2600_common *hw_priv) +{ + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return; + } + + mutex_lock(&hw_priv->bes_power.pwr_mutex); + hw_priv->bes_power.sys_resume_task = current; + bes2600_pwr_device_exit_lp_mode(hw_priv); + mutex_unlock(&hw_priv->bes_power.pwr_mutex); +} + +void bes2600_pwr_resume_end(struct bes2600_common *hw_priv) +{ + unsigned long max_timeout = 0; + bool constant_event_exist = false; + unsigned long flags; + + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return ; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + mutex_lock(&hw_priv->bes_power.pwr_mutex); + if(!constant_event_exist && max_timeout == 0) + bes2600_pwr_device_enter_lp_mode(hw_priv); + hw_priv->bes_power.sys_resume_task = NULL; + mutex_unlock(&hw_priv->bes_power.pwr_mutex); +} + +void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv) +{ + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return; + } + + if (bes2600_pwr_device_is_idle(hw_priv)) { + if(!work_pending(&hw_priv->bes_power.power_mcu_down_work)) { + queue_work(hw_priv->workqueue, &hw_priv->bes_power.power_mcu_down_work); + } + } +} + +void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv) +{ + if(atomic_read(&hw_priv->bes_power.dev_state) == 0) { + return; + } + + bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_AP_LP_BAD, 100); + hw_priv->bes_power.ap_lp_bad = true; + bes2600_info(BES2600_DBG_PWR, "mark ap_lp_bad flag\n"); +} + +void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv) +{ + hw_priv->bes_power.ap_lp_bad = false; + bes2600_info(BES2600_DBG_PWR, "clear ap_lp_bad flag\n"); +} + +int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len) +{ + unsigned long max_timeout = 0; + int used_len = 0; + struct bes2600_pwr_event_t *item = NULL; + unsigned long flags; + + if(!buffer) { + return -1; + } + + spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags); + bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout); + used_len = snprintf(buffer, buf_len, "Event \t\tFlag\t\ttimeout(ticks)\n"); + if(!list_empty(&hw_priv->bes_power.pending_event_list)) { + list_for_each_entry(item, &hw_priv->bes_power.pending_event_list, link) { + used_len += snprintf(buffer + used_len, buf_len - used_len, "%9s\t\t%s\t\t%lu\n", + bes2600_get_pwr_busy_event_name(item), + BES_PWR_IS_CONSTANT_EVENT(item->event) ? "C" : "D", + bes2600_get_pwr_busy_event_timeout(item)); + } + } + spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags); + + return 0; +} diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h new file mode 100644 index 000000000000..2c27f4293a55 --- /dev/null +++ b/drivers/staging/bes2600/bes_pwr.h @@ -0,0 +1,167 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __BES_PWR_H__ +#define __BES_PWR_H__ + +#include "bes2600.h" + +enum bes2600_pwr_event_type { + BES_PWR_LOCK_ON_SCAN = 1, + BES_PWR_LOCK_ON_JOIN, + BES_PWR_LOCK_ON_TX, + BES_PWR_LOCK_ON_RX, + BES_PWR_LOCK_ON_FLUSH, + BES_PWR_LOCK_ON_ROC, + BES_PWR_LOCK_ON_WSM_TX, + BES_PWR_LOCK_ON_WSM_OPER, + BES_PWR_LOCK_ON_BSS_LOST, + BES_PWR_LOCK_ON_GET_IP, + BES_PWR_LOCK_ON_PS_ACTIVE, + BES_PWR_LOCK_ON_LMAC_RSP, + BES_PWR_LOCK_ON_AP, + BES_PWR_LOCK_ON_TEST_CMD, + BES_PWR_LOCK_ON_MUL_REQ, + BES_PWR_LOCK_ON_ADV_SCAN, + BES_PWR_LOCK_ON_DISCON, + BES_PWR_LOCK_ON_QUEUE_GC, + BES_PWR_LOCK_ON_AP_LP_BAD, + /* add new lock event here */ + BES_PWR_LOCK_ON_EVENT_MAX, +}; + +#define BES_PWR_IS_CONSTANT_EVENT(x) ((x) & 0x80000000) +#define BES_PWR_EVENT_SET_CONSTANT(x) (x = (x | 0x80000000)) +#define BES_PWR_EVENT_NUMBER(x) ((x) & 0x7fffffff) + +#define BES2600_POWER_DOWN_DELAY 50 // unit in millisecond +#define BES2600_DELAY_EVENT_NUM (BES_PWR_LOCK_ON_EVENT_MAX << 1) + +#define BES_PWR_EVENT_TX_TIMEOUT 1500 // unit in millisecond +#define BES_PWR_EVENT_RX_TIMEOUT 100 // unit in millisecond + +struct bes2600_pwr_event_t +{ + struct list_head link; + unsigned long timeout; + unsigned long delay; + u8 idx; + enum bes2600_pwr_event_type event; +}; + +enum power_down_state +{ + POWER_DOWN_STATE_LOCKED = 0, + POWER_DOWN_STATE_LOCKING, + POWER_DOWN_STATE_UNLOCKING, + POWER_DOWN_STATE_UNLOCKED, +}; + +typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv); +typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv); + +struct bes2600_pwr_enter_cb_item +{ + struct list_head link; + bes_pwr_enter_lp_cb cb; +}; + +struct bes2600_pwr_exit_cb_item +{ + struct list_head link; + bes_pwr_exit_lp_cb cb; +}; + +struct bes2600_pwr_t +{ + spinlock_t pwr_lock; + struct delayed_work power_down_work; + struct work_struct power_async_work; + struct work_struct power_mcu_down_work; + struct list_head async_timeout_list; + struct list_head pending_event_list; + struct list_head free_event_list; + enum power_down_state power_state; + struct task_struct *power_down_task; + struct task_struct *power_up_task; + struct task_struct *sys_suspend_task; + struct task_struct *sys_resume_task; + struct semaphore sync_lock; + bool pending_lock; + atomic_t dev_state; + struct mutex pwr_mutex; + struct bes2600_common *hw_priv; + struct completion pm_enter_cmpl; + struct mutex pwr_cb_mutex; + struct list_head enter_cb_list; + struct list_head exit_cb_list; + wait_queue_head_t dev_lp_wq; + bool ap_lp_bad; + struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM]; + atomic_t pm_set_in_process; +}; + +#ifdef CONFIG_BES2600_WOWLAN +void bes2600_pwr_init(struct bes2600_common *hw_priv); +void bes2600_pwr_exit(struct bes2600_common *hw_priv); +void bes2600_pwr_prepare(struct bes2600_common *hw_priv); +void bes2600_pwr_complete(struct bes2600_common *hw_priv); +void bes2600_pwr_start(struct bes2600_common *hw_priv); +void bes2600_pwr_stop(struct bes2600_common *hw_priv); +bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event); +int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event); +int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event); +int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout); +int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout); +int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event); +void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode); +bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv); +void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb); +void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb); +void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb); +void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb); +void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv); +void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv); +void bes2600_pwr_resume_start(struct bes2600_common *hw_priv); +void bes2600_pwr_resume_end(struct bes2600_common *hw_priv); +void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv); +void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv); +void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv); +int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len); +#else +static inline void bes2600_pwr_init(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_exit(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_prepare(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_complete(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_start(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_stop(struct bes2600_common *hw_priv) { } +static inline bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event) { return false; } +static inline int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event) { return 0; } +static inline int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event) {return 0; } +static inline int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout) { return 0; } +static inline int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout) { return 0; } +static inline int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event) { return 0; } +static inline void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) { } +static inline bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv) { return false; } +static inline void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { } +static inline void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { } +static inline void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { } +static inline void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { } +static inline void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_resume_start(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_resume_end(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv) { } +static inline void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv) { } +static inline int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len) { return 0; } +#endif + +#endif \ No newline at end of file diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c new file mode 100644 index 000000000000..fed5101d42c6 --- /dev/null +++ b/drivers/staging/bes2600/bh.c @@ -0,0 +1,1654 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include "bes2600.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "sbus.h" +#include "debug.h" +#include "epta_coex.h" +#include "bes_chardev.h" +#include "txrx_opt.h" +#include "sta.h" + +static int bes2600_bh(void *arg); + +#ifdef CONFIG_BES2600_WLAN_SDIO +extern void sdio_work_debug(struct sbus_priv *self); +#endif + +/* TODO: Verify these numbers with WSM specification. */ +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +/* Suspend state privates */ +enum bes2600_bh_pm_state { + BES2600_BH_RESUMED = 0, + BES2600_BH_SUSPEND, + BES2600_BH_SUSPENDED, + BES2600_BH_RESUME, +}; + +typedef int (*bes2600_wsm_handler)(struct bes2600_common *hw_priv, + u8 *data, size_t size); + +#ifdef MCAST_FWDING +int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count); +#endif + +static void bes2600_bh_work(struct work_struct *work) +{ + struct bes2600_common *priv = + container_of(work, struct bes2600_common, bh_work); + bes2600_bh(priv); +} + +int bes2600_register_bh(struct bes2600_common *hw_priv) +{ + int err = 0; + /* Realtime workqueue */ + hw_priv->bh_workqueue = alloc_workqueue("bes2600_bh", + WQ_MEM_RECLAIM | WQ_HIGHPRI + | WQ_CPU_INTENSIVE, 1); + + if (!hw_priv->bh_workqueue) + return -ENOMEM; + + INIT_WORK(&hw_priv->bh_work, bes2600_bh_work); + + bes2600_info(BES2600_DBG_BH, "[BH] register.\n"); + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE +#ifdef WIFI_BT_COEXIST_EPTA_FDD + coex_init_mode(hw_priv, WIFI_COEX_MODE_FDD_BIT); +#else + coex_init_mode(hw_priv, 0); +#endif +#endif + atomic_set(&hw_priv->bh_rx, 0); + atomic_set(&hw_priv->bh_tx, 0); + atomic_set(&hw_priv->bh_term, 0); + atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); + hw_priv->buf_id_tx = 0; + hw_priv->buf_id_rx = 0; + init_waitqueue_head(&hw_priv->bh_wq); + init_waitqueue_head(&hw_priv->bh_evt_wq); + + err = !queue_work(hw_priv->bh_workqueue, &hw_priv->bh_work); + WARN_ON(err); + return err; +} + +void bes2600_unregister_bh(struct bes2600_common *hw_priv) +{ +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + coex_deinit_mode(hw_priv); +#endif + + atomic_add(1, &hw_priv->bh_term); + wake_up(&hw_priv->bh_wq); + + flush_workqueue(hw_priv->bh_workqueue); + + destroy_workqueue(hw_priv->bh_workqueue); + hw_priv->bh_workqueue = NULL; + + bes2600_info(BES2600_DBG_BH, "[BH] unregistered.\n"); +} + +void bes2600_irq_handler(struct bes2600_common *hw_priv) +{ + bes2600_dbg(BES2600_DBG_BH, "[BH] irq.\n"); + if(!hw_priv) { + bes2600_warn(BES2600_DBG_BH, "%s hw private data is null", __func__); + return; + } + + if (hw_priv->bh_error) { + bes2600_err(BES2600_DBG_BH, "%s bh error", __func__); + return; + } + + if (atomic_add_return(1, &hw_priv->bh_rx) == 1) + wake_up(&hw_priv->bh_wq); +} +EXPORT_SYMBOL(bes2600_irq_handler); + +void bes2600_bh_wakeup(struct bes2600_common *hw_priv) +{ + bes2600_dbg(BES2600_DBG_BH, "[BH] wakeup.\n"); + if (WARN_ON(hw_priv->bh_error)) + return; + + if (atomic_add_return(1, &hw_priv->bh_tx) == 1) + wake_up(&hw_priv->bh_wq); +} +EXPORT_SYMBOL(bes2600_bh_wakeup); + +int bes2600_bh_suspend(struct bes2600_common *hw_priv) +{ +#ifdef MCAST_FWDING + int i =0; + struct bes2600_vif *priv = NULL; +#endif + + bes2600_dbg(BES2600_DBG_BH, "[BH] suspend.\n"); + if (hw_priv->bh_error) { + wiphy_warn(hw_priv->hw->wiphy, "BH error -- can't suspend\n"); + return -EINVAL; + } + +#ifdef MCAST_FWDING + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if ( (priv->multicast_filter.enable) + && (priv->join_status == BES2600_JOIN_STATUS_AP) ) { + wsm_release_buffer_to_fw(priv, + (hw_priv->wsm_caps.numInpChBufs - 1)); + break; + } + } +#endif + + atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPEND); + wake_up(&hw_priv->bh_wq); + return wait_event_timeout(hw_priv->bh_evt_wq, hw_priv->bh_error || + (BES2600_BH_SUSPENDED == atomic_read(&hw_priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} +EXPORT_SYMBOL(bes2600_bh_suspend); + +int bes2600_bh_resume(struct bes2600_common *hw_priv) +{ + int ret; + +#ifdef MCAST_FWDING + int i =0; + struct bes2600_vif *priv = NULL; +#endif + + bes2600_dbg(BES2600_DBG_BH, "[BH] resume.\n"); + if (hw_priv->bh_error) { + wiphy_warn(hw_priv->hw->wiphy, "BH error -- can't resume\n"); + return -EINVAL; + } + + atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUME); + wake_up(&hw_priv->bh_wq); + ret = wait_event_timeout(hw_priv->bh_evt_wq, hw_priv->bh_error || + (BES2600_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; + +#ifdef MCAST_FWDING + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if ((priv->join_status == BES2600_JOIN_STATUS_AP) + && (priv->multicast_filter.enable)) { + u8 count = 0; + WARN_ON(wsm_request_buffer_request(priv, &count)); + bes2600_dbg(BES2600_DBG_BH, "[BH] BH resume. Reclaim Buff %d \n",count); + break; + } + } +#endif + + return ret; +} +EXPORT_SYMBOL(bes2600_bh_resume); + +static inline void wsm_alloc_tx_buffer(struct bes2600_common *hw_priv) +{ + ++hw_priv->hw_bufs_used; +} + +int wsm_release_tx_buffer(struct bes2600_common *hw_priv, int count) +{ + int ret = 0; + int hw_bufs_used = hw_priv->hw_bufs_used; + + hw_priv->hw_bufs_used -= count; + + if (WARN_ON(hw_priv->hw_bufs_used < 0)) + ret = -1; + /* Tx data patch stops when all but one hw buffers are used. + So, re-start tx path in case we find hw_bufs_used equals + numInputChBufs - 1. + */ + else if (hw_bufs_used >= (hw_priv->wsm_caps.numInpChBufs - 1)) + ret = 1; + if (!hw_priv->hw_bufs_used) { + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_LMAC_RSP); + wake_up(&hw_priv->bh_evt_wq); + } + return ret; +} +EXPORT_SYMBOL(wsm_release_tx_buffer); + +int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id, + int count) +{ + int ret = 0; + + hw_priv->hw_bufs_used_vif[if_id] -= count; + + if (!hw_priv->hw_bufs_used_vif[if_id]) + wake_up(&hw_priv->bh_evt_wq); + + if (WARN_ON(hw_priv->hw_bufs_used_vif[if_id] < 0)) + ret = -1; + return ret; +} +#ifdef MCAST_FWDING +int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count) +{ + int i; + u8 flags; + struct wsm_buf *buf; + size_t buf_len; + struct wsm_hdr *wsm; + struct bes2600_common *hw_priv = priv->hw_priv; + +#if 1 + if (priv->join_status != BES2600_JOIN_STATUS_AP) { + return 0; + } +#endif + bes2600_dbg(BES2600_DBG_BH, "Rel buffer to FW %d, %d\n", count, hw_priv->hw_bufs_used); + + for (i = 0; i < count; i++) { + if ((hw_priv->hw_bufs_used + 1) < hw_priv->wsm_caps.numInpChBufs) { + flags = i ? 0: 0x1; + + wsm_alloc_tx_buffer(hw_priv); + + buf = &hw_priv->wsm_release_buf[i]; + buf_len = buf->data - buf->begin; + + /* Add sequence number */ + wsm = (struct wsm_hdr *)buf->begin; + BUG_ON(buf_len < sizeof(*wsm)); + + wsm->id &= __cpu_to_le32( + ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= cpu_to_le32( + WSM_TX_SEQ(hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)])); + + bes2600_dbg(BES2600_DBG_BH, "REL %d\n", hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)]); + if (WARN_ON(bes2600_data_write(hw_priv, + buf->begin, buf_len))) { + break; + } + hw_priv->buf_released = 1; + hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] = + (hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] + 1) & WSM_TX_SEQ_MAX; + } else + break; + } + + if (i == count) { + return 0; + } + + /* Should not be here */ + bes2600_err(BES2600_DBG_BH, "[BH] Less HW buf %d,%d.\n", hw_priv->hw_bufs_used, + hw_priv->wsm_caps.numInpChBufs); + WARN_ON(1); + + return -1; +} +#endif + +#if 0 +static struct sk_buff *bes2600_get_skb(struct bes2600_common *hw_priv, size_t len) +{ + struct sk_buff *skb; + size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; + + if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) { + skb = dev_alloc_skb(alloc_len + + WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV + MIC */ + - 2 /* Piggyback */); + /* In AP mode RXed SKB can be looped back as a broadcast. + * Here we reserve enough space for headers. */ + skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + - WSM_RX_EXTRA_HEADROOM); + } else { + skb = hw_priv->skb_cache; + hw_priv->skb_cache = NULL; + } + return skb; +} + +static void bes2600_put_skb(struct bes2600_common *hw_priv, struct sk_buff *skb) +{ + if (hw_priv->skb_cache) + dev_kfree_skb(skb); + else + hw_priv->skb_cache = skb; +} + +static int bes2600_bh_read_ctrl_reg(struct bes2600_common *hw_priv, + u16 *ctrl_reg) +{ + int ret; + + ret = bes2600_reg_read_16(hw_priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = bes2600_reg_read_16(hw_priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) + bes2600_err(BES2600_DBG_BH, "[BH] Failed to read control register.\n"); + } + + return ret; +} + +static int bes2600_device_wakeup(struct bes2600_common *hw_priv) +{ + u16 ctrl_reg; + int ret; + + bes2600_dbg(BES2600_DBG_BH, "[BH] Device wakeup.\n"); + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT); + if (WARN_ON(ret)) + return ret; + + ret = bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if (WARN_ON(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. */ + if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { + bes2600_dbg(BES2600_DBG_BH, "[BH] Device awake.\n"); + return 1; + } + + return 0; +} + +#endif + +/* Must be called from BH thraed. */ +void bes2600_enable_powersave(struct bes2600_vif *priv, + bool enable) +{ + bes2600_dbg(BES2600_DBG_BH, "[BH] Powerave is %s.\n", + enable ? "enabled" : "disabled"); + priv->powersave_enabled = enable; +} + +#if 0 +#define INTERRUPT_WORKAROUND +static int bes2600_bh(void *arg) +{ + struct bes2600_common *hw_priv = arg; + struct bes2600_vif *priv = NULL; + struct sk_buff *skb_rx = NULL; + size_t read_len = 0; + int rx, tx, term, suspend; + struct wsm_hdr *wsm; + size_t wsm_len; + int wsm_id; + u8 wsm_seq; + int rx_resync = 1; + u16 ctrl_reg = 0; + int tx_allowed; + int pending_tx = 0; + int tx_burst; + int rx_burst = 0; + long status; +#if defined(CONFIG_BES2600_WSM_DUMPS) + size_t wsm_dump_max = -1; +#endif + u32 dummy; + bool powersave_enabled; + int i; + int vif_selected; + + for (;;) { + powersave_enabled = 1; + spin_lock(&hw_priv->vif_list_lock); + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i = (CW12XX_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + powersave_enabled &= !!priv->powersave_enabled; + } + spin_unlock(&hw_priv->vif_list_lock); + if (!hw_priv->hw_bufs_used + && powersave_enabled + && !hw_priv->device_can_sleep + && !atomic_read(&hw_priv->recent_scan)) { + status = HZ/8; + bes2600_dbg(BES2600_DBG_BH, "[BH] No Device wakedown.\n"); +#ifndef FPGA_SETUP + WARN_ON(bes2600_reg_write_16(hw_priv, + ST90TDS_CONTROL_REG_ID, 0)); + hw_priv->device_can_sleep = true; +#endif + } else if (hw_priv->hw_bufs_used) + /* Interrupt loss detection */ + status = HZ/8; + else + status = HZ/8; + + /* Dummy Read for SDIO retry mechanism*/ + if (((atomic_read(&hw_priv->bh_rx) == 0) && + (atomic_read(&hw_priv->bh_tx) == 0))) + bes2600_reg_read(hw_priv, ST90TDS_CONFIG_REG_ID, + &dummy, sizeof(dummy)); +#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT) + wsm_dump_max = hw_priv->wsm_dump_max_size; +#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */ + +#ifdef INTERRUPT_WORKAROUND + /* If a packet has already been txed to the device then read the + control register for a probable interrupt miss before going + further to wait for interrupt; if the read length is non-zero + then it means there is some data to be received */ + if (hw_priv->hw_bufs_used) { + bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + { + rx = 1; + goto test; + } + } +#endif + + status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({ + rx = atomic_xchg(&hw_priv->bh_rx, 0); + tx = atomic_xchg(&hw_priv->bh_tx, 0); + term = atomic_xchg(&hw_priv->bh_term, 0); + suspend = pending_tx ? + 0 : atomic_read(&hw_priv->bh_suspend); + (rx || tx || term || suspend || hw_priv->bh_error); + }), status); + + if (status < 0 || term || hw_priv->bh_error) + break; + +#ifdef INTERRUPT_WORKAROUND + if (!status) { + bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + { + bes2600_err(BES2600_DBG_BH, "MISS 1\n"); + rx = 1; + goto test; + } + } +#endif + if (!status && hw_priv->hw_bufs_used) { + unsigned long timestamp = jiffies; + long timeout; + bool pending = false; + int i; + + wiphy_warn(hw_priv->hw->wiphy, "Missed interrupt?\n"); + rx = 1; + + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= bes2600_queue_get_xmit_timestamp( + &hw_priv->tx_queue[i], + ×tamp, -1, + hw_priv->pending_frame_id); + + /* Check if frame transmission is timed out. + * Add an extra second with respect to possible + * interrupt loss. */ + timeout = timestamp + + WSM_CMD_LAST_CHANCE_TIMEOUT + + 1 * HZ - + jiffies; + + /* And terminate BH tread if the frame is "stuck" */ + if (pending && timeout < 0) { + //wiphy_warn(priv->hw->wiphy, + // "Timeout waiting for TX confirm.\n"); + bes2600_info(BES2600_DBG_BH, "bes2600_bh: Timeout waiting for TX confirm.\n"); + break; + } + +#if defined(CONFIG_BES2600_DUMP_ON_ERROR) + BUG_ON(1); +#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ + } else if (!status) { + if (!hw_priv->device_can_sleep + && !atomic_read(&hw_priv->recent_scan)) { + bes2600_dbg(BES2600_DBG_BH, "[BH] Device wakedown. Timeout.\n"); +#ifndef FPGA_SETUP + WARN_ON(bes2600_reg_write_16(hw_priv, + ST90TDS_CONTROL_REG_ID, 0)); + hw_priv->device_can_sleep = true; +#endif + } + continue; + } else if (suspend) { + bes2600_dbg(BES2600_DBG_BH, "[BH] Device suspend.\n"); + powersave_enabled = 1; + spin_lock(&hw_priv->vif_list_lock); + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i = (CW12XX_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + powersave_enabled &= !!priv->powersave_enabled; + } + spin_unlock(&hw_priv->vif_list_lock); + if (powersave_enabled) { + bes2600_dbg(BES2600_DBG_BH, "[BH] No Device wakedown. Suspend.\n"); +#ifndef FPGA_SETUP + WARN_ON(bes2600_reg_write_16(hw_priv, + ST90TDS_CONTROL_REG_ID, 0)); + hw_priv->device_can_sleep = true; +#endif + } + + atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED); + wake_up(&hw_priv->bh_evt_wq); + status = wait_event_interruptible(hw_priv->bh_wq, + BES2600_BH_RESUME == atomic_read( + &hw_priv->bh_suspend)); + if (status < 0) { + wiphy_err(hw_priv->hw->wiphy, + "%s: Failed to wait for resume: %ld.\n", + __func__, status); + break; + } + bes2600_dbg(BES2600_DBG_BH, "[BH] Device resume.\n"); + atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); + wake_up(&hw_priv->bh_evt_wq); + atomic_add(1, &hw_priv->bh_rx); + continue; + } + +test: + tx += pending_tx; + pending_tx = 0; + + if (rx) { + size_t alloc_len; + u8 *data; + +#ifdef INTERRUPT_WORKAROUND + if(!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)) +#endif + if (WARN_ON(bes2600_bh_read_ctrl_reg( + hw_priv, &ctrl_reg))) + break; +rx: + read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; + if (!read_len) { + rx_burst = 0; + goto tx; + } + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + bes2600_dbg(BES2600_DBG_BH, "Invalid read len: %d", + read_len); + break; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB */ + read_len = read_len + 2; + +#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) + alloc_len = hw_priv->sbus_ops->align_size( + hw_priv->sbus_priv, read_len); +#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ + /* Platform's SDIO workaround */ + alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); + if (read_len & (SDIO_BLOCK_SIZE - 1)) + alloc_len += SDIO_BLOCK_SIZE; +#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ + + /* Check if not exceeding BES2600 capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + bes2600_dbg(BES2600_DBG_BH, "Read aligned len: %d\n", + alloc_len); + } + + skb_rx = bes2600_get_skb(hw_priv, alloc_len); + if (WARN_ON(!skb_rx)) + break; + + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (WARN_ON(!data)) + break; + + if (WARN_ON(bes2600_data_read(hw_priv, data, alloc_len))) + break; + + /* Piggyback */ + ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + wsm = (struct wsm_hdr *)data; + wsm_len = __le32_to_cpu(wsm->len); + if (WARN_ON(wsm_len > read_len)) + break; + +#if defined(CONFIG_BES2600_WSM_DUMPS) + if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { + u16 msgid, ifid; + u16 *p = (u16 *)data; + msgid = (*(p + 1)) & 0xC3F; + ifid = (*(p + 1)) >> 6; + ifid &= 0xF; + bes2600_dbg(BES2600_DBG_BH, "[DUMP] <<< msgid 0x%.4X ifid %d len %d\n", + msgid, ifid, *p); + print_hex_dump_bytes("<-- ", + DUMP_PREFIX_NONE, + data, min(wsm_len, wsm_dump_max)); + } +#endif /* CONFIG_BES2600_WSM_DUMPS */ + + wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb_rx, wsm_len); + + if (unlikely(wsm_id == 0x0800)) { + wsm_handle_exception(hw_priv, + &data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + break; + } else if (unlikely(!rx_resync)) { + if (WARN_ON(wsm_seq != hw_priv->wsm_rx_seq)) { +#if defined(CONFIG_BES2600_DUMP_ON_ERROR) + BUG_ON(1); +#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ + break; + } + } + hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; + + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(hw_priv, 1); + if (WARN_ON(rc < 0)) + break; + else if (rc > 0) + tx = 1; + } + + /* bes2600_wsm_rx takes care on SKB livetime */ + if (WARN_ON(wsm_handle_rx(hw_priv, wsm_id, wsm, + &skb_rx))) + break; + + if (skb_rx) { + bes2600_put_skb(hw_priv, skb_rx); + skb_rx = NULL; + } + + read_len = 0; + + if (rx_burst) { + bes2600_debug_rx_burst(hw_priv); + --rx_burst; + goto rx; + } + } + +tx: + BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); + tx_burst = hw_priv->wsm_caps.numInpChBufs - + hw_priv->hw_bufs_used; + tx_allowed = tx_burst > 0; + if (tx && tx_allowed) { + size_t tx_len; + u8 *data; + int ret; + + if (hw_priv->device_can_sleep) { + ret = bes2600_device_wakeup(hw_priv); + if (WARN_ON(ret < 0)) + break; + else if (ret) + hw_priv->device_can_sleep = false; + else { + /* Wait for "awake" interrupt */ + pending_tx = tx; + continue; + } + } + + wsm_alloc_tx_buffer(hw_priv); + ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst, + &vif_selected); + if (ret <= 0) { + wsm_release_tx_buffer(hw_priv, 1); + if (WARN_ON(ret < 0)) + break; + } else { + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le32_to_cpu(wsm->len) != tx_len); + +#if 0 /* count is not implemented */ + if (ret > 1) + atomic_add(1, &hw_priv->bh_tx); +#else + atomic_add(1, &hw_priv->bh_tx); +#endif + +#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) + if (tx_len <= 8) + tx_len = 16; + tx_len = hw_priv->sbus_ops->align_size( + hw_priv->sbus_priv, tx_len); +#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ + /* HACK!!! Platform limitation. + * It is also supported by upper layer: + * there is always enough space at the + * end of the buffer. */ + if (tx_len & (SDIO_BLOCK_SIZE - 1)) { + tx_len &= ~(SDIO_BLOCK_SIZE - 1); + tx_len += SDIO_BLOCK_SIZE; + } +#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ + + /* Check if not exceeding BES2600 + capabilities */ + if (WARN_ON_ONCE( + tx_len > EFFECTIVE_BUF_SIZE)) { + bes2600_dbg(BES2600_DBG_BH, "Write aligned len:" + " %d\n", tx_len); + } + + wsm->id &= __cpu_to_le32( + ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= cpu_to_le32(WSM_TX_SEQ( + hw_priv->wsm_tx_seq)); + + if (WARN_ON(bes2600_data_write(hw_priv, + data, tx_len))) { + wsm_release_tx_buffer(hw_priv, 1); + break; + } + + if (vif_selected != -1) { + hw_priv->hw_bufs_used_vif[ + vif_selected]++; + } + +#if defined(CONFIG_BES2600_WSM_DUMPS) + if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { + u16 msgid, ifid; + u16 *p = (u16 *)data; + msgid = (*(p + 1)) & 0x3F; + ifid = (*(p + 1)) >> 6; + ifid &= 0xF; + if (msgid == 0x0006) { + bes2600_dbg(BES2600_DBG_BH, "[DUMP] >>> " + "msgid 0x%.4X " + "ifid %d len %d" + " MIB 0x%.4X\n", + msgid, ifid, + *p, *(p + 2)); + } else { + bes2600_dbg(BES2600_DBG_BH, "[DUMP] >>> " + "msgid 0x%.4X " + "ifid %d " + "len %d\n", + msgid, ifid, + *p); + } + print_hex_dump_bytes("--> ", + DUMP_PREFIX_NONE, + data, + min(__le32_to_cpu(wsm->len), + wsm_dump_max)); + } +#endif /* CONFIG_BES2600_WSM_DUMPS */ + + wsm_txed(hw_priv, data); + hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) + & WSM_TX_SEQ_MAX; + + if (tx_burst > 1) { + bes2600_debug_tx_burst(hw_priv); + ++rx_burst; + goto tx; + } + } + } + + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + goto rx; + } + + if (skb_rx) { + bes2600_put_skb(hw_priv, skb_rx); + skb_rx = NULL; + } + + + if (!term) { + bes2600_dbg(BES2600_DBG_ERROR, "[BH] Fatal error, exitting.\n"); +#if defined(CONFIG_BES2600_DUMP_ON_ERROR) + BUG_ON(1); +#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ + hw_priv->bh_error = 1; +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + spin_lock(&hw_priv->vif_list_lock); + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + } + spin_unlock(&hw_priv->vif_list_lock); + bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ); +#endif + /* TODO: schedule_work(recovery) */ +#ifndef HAS_PUT_TASK_STRUCT + /* The only reason of having this stupid code here is + * that __put_task_struct is not exported by kernel. */ + for (;;) { + int status = wait_event_interruptible(hw_priv->bh_wq, ({ + term = atomic_xchg(&hw_priv->bh_term, 0); + (term); + })); + + if (status || term) + break; + } +#endif + } + return 0; +} +#else + +#ifdef CONFIG_BES2600_WLAN_SDIO +extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg); +#endif + +static void bes2600_bh_parse_ipv4_data(struct iphdr *ip) +{ + u8 *tmp_ptr = (u8 *)ip; + + bes2600_info(BES2600_DBG_BH, "IP Addr src:0x%08x dst:0x%08x\n", + __be32_to_cpu(ip->saddr), __be32_to_cpu(ip->daddr)); + + if (ip->protocol == IPPROTO_TCP) { + struct tcphdr *tcp = (struct tcphdr *)(tmp_ptr + ip->ihl * 4); + bes2600_info(BES2600_DBG_BH, "TCP Port src:%d dst:%d\n", + __be16_to_cpu(tcp->source), __be16_to_cpu(tcp->dest)); + } else if (ip->protocol == IPPROTO_UDP) { + struct udphdr *udp = (struct udphdr *)(tmp_ptr + ip->ihl * 4); + bes2600_info(BES2600_DBG_BH, "UDP Port src:%d dst:%d\n", + __be16_to_cpu(udp->source), __be16_to_cpu(udp->dest)); + } +} + +static void bes2600_bh_parse_data_pkt(struct bes2600_common *hw_priv, struct sk_buff *skb) +{ + struct wsm_hdr *wsm = (struct wsm_hdr *)skb->data; + u16 wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + int if_id = (wsm_id >> 6) & 0x0F; + u8 *data_ptr = (u8 *)&wsm[1]; + struct ieee80211_hdr *i80211_ptr = (struct ieee80211_hdr *)(data_ptr + 28 /* radio header */); + __le16 fctl = *(__le16 *)i80211_ptr; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); + u32 encry_hdr_len = bes2600_bh_get_encry_hdr_len(priv->cipherType); + u32 i80211_len = ieee80211_hdrlen(fctl); + u8 *tmp_ptr = (u8 *)i80211_ptr; + u16 *eth_type_ptr = (u16 *)(tmp_ptr + i80211_len + encry_hdr_len + ETH_ALEN); + u16 eth_type = __be16_to_cpu(*eth_type_ptr); + + bes2600_info(BES2600_DBG_BH, "Host was waked by data:\n"); + bes2600_info(BES2600_DBG_BH, "RA:%pM\n", ieee80211_get_DA(i80211_ptr)); + bes2600_info(BES2600_DBG_BH, "ETH_TYPE:0x%04x\n", eth_type); + + if (eth_type == ETH_P_IP) { + struct iphdr *ip = (struct iphdr *)ð_type_ptr[1]; + bes2600_info(BES2600_DBG_BH, "IPVersion:%d\n", ip->version); + bes2600_info(BES2600_DBG_BH, "IPProtol:%d\n", ip->protocol); + + if (ip->version == 4) { + bes2600_bh_parse_ipv4_data(ip); + } + } +} + +static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct sk_buff *skb) +{ + struct wsm_hdr *wsm = (struct wsm_hdr *)skb->data; + u16 wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + + if (hw_priv->sbus_ops->wakeup_source && + hw_priv->sbus_ops->wakeup_source(hw_priv->sbus_priv)) { + if (wsm_id == 0x0804) { + u8 *data_ptr = (u8 *)&wsm[1]; + u8 *i80211_ptr = data_ptr + 28/* radio header */; + __le16 fctl = *(__le16 *)i80211_ptr; + + if (ieee80211_is_mgmt(fctl)) { + u16 type = (fctl & cpu_to_le16(IEEE80211_FCTL_FTYPE)) >> 2; + u16 stype = (fctl & cpu_to_le16(IEEE80211_FCTL_STYPE)) >> 4; + bes2600_info(BES2600_DBG_BH, "Host was waked by mgmt, type:%d(%d)\n", type, stype); + } else if(ieee80211_is_data(fctl)){ + bes2600_bh_parse_data_pkt(hw_priv, skb); + } else { + bes2600_info(BES2600_DBG_BH, "Host was waked by unexpected frame, fctl:0x%04x\n", fctl); + } + } else { + bes2600_info(BES2600_DBG_BH, "Host was waked by event:0x%04x.\n", wsm_id); + } + } +} + +static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx) +{ + struct sk_buff *skb = NULL; + struct wsm_hdr *wsm; + size_t wsm_len; + u16 wsm_id; + u8 wsm_seq; + int rx = 0; + u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */ + +#if defined(CONFIG_BES2600_WLAN_USB) || defined(CONFIG_BES2600_WLAN_SPI) || defined(BES_SDIO_RX_MULTIPLE_ENABLE) + skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv); + if (!skb) + return 0; + rx = 1; // always consider rx pipe not empty +#else + u32 ctrl_reg = 0; + size_t read_len = 0; +// int rx_resync = 1; + size_t alloc_len; + u8 *data; + + bes2600_bh_read_ctrl_reg(priv, &ctrl_reg); + + read_len = (ctrl_reg & BES_TX_NEXT_LEN_MASK); + + if (!read_len) + return 0; /* No more work */ + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + bes2600_err(BES2600_DBG_BH, "Invalid read len: %zu (%04x)", + read_len, ctrl_reg); + goto err; + } + + /* more 2 byte is not needed ? */ +#if 0 + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB + */ + read_len = read_len + 2; +#endif + + alloc_len = priv->sbus_ops->align_size( + priv->sbus_priv, read_len); + + /* Check if not exceeding BES2600 capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + bes2600_dbg(BES2600_DBG_BH, "Read aligned len: %zu\n", + alloc_len); + } + + skb = dev_alloc_skb(alloc_len); + if (WARN_ON(!skb)) + goto err; + + skb_trim(skb, 0); + skb_put(skb, read_len); + data = skb->data; + if (WARN_ON(!data)) + goto err; + + if (WARN_ON(bes2600_data_read(priv, data, alloc_len))) { + bes2600_err(BES2600_DBG_BH, "rx blew up, len %zu\n", alloc_len); + goto err; + } + + /* piggyback is not implemented, + * and only recieve data once + */ +#if 0 + /* Piggyback */ + ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + /* check if more data need to recv */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + rx = 1; +#else + rx = 0; +#endif + +#endif + + wsm = (struct wsm_hdr *)skb->data; + wsm_len = __le16_to_cpu(wsm->len); + if (WARN_ON(wsm_len > skb->len)) { + bes2600_err(BES2600_DBG_BH, "wsm_len err %d %d\n", (int)wsm_len, (int)skb->len); + goto err; + } + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("<-- ", + DUMP_PREFIX_NONE, + skb->data, wsm_len); + + wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; + bes2600_dbg(BES2600_DBG_BH, "bes2600_bh_rx_helper wsm_id:0x%04x seq:%d\n", wsm_id, wsm_seq); + + skb_trim(skb, wsm_len); + + if (wsm_id == 0x0800) { + wsm_handle_exception(priv, + &skb->data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + bes2600_err(BES2600_DBG_BH, "wsm exception.!\n"); + goto err; + } else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) { + bes2600_err(BES2600_DBG_BH, "seq error! %u. %u. 0x%x.", wsm_seq, priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id); + goto err; + } + + bes2600_bh_parse_wakeup_event(priv, skb); + + priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7; + + if (IS_DRIVER_TO_MCU_CMD(wsm_id)) + confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label); + + if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) { + int rc = wsm_release_tx_buffer(priv, 1); + bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id)); + + if (WARN_ON(rc < 0)) + return rc; + else if (rc > 0) + *tx = 1; + } + + /* bes2600_wsm_rx takes care on SKB livetime */ + //if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb))) + if ((wsm_handle_rx(priv, wsm_id, wsm, &skb))) { + bes2600_err(BES2600_DBG_BH, "wsm_handle_rx fail\n"); + goto err; + } + + if (skb) { + dev_kfree_skb(skb); + skb = NULL; + } + return rx; + +err: + bes2600_err(BES2600_DBG_BH, "bes2600_bh_rx_helper err\n"); + if (skb) { + dev_kfree_skb(skb); + skb = NULL; + } + return -1; +} + +static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv, + int *pending_tx, + int *tx_burst) +{ + size_t tx_len; + u8 *data; + int ret; + struct wsm_hdr *wsm; + int vif_selected; + +#ifdef CONFIG_BES2600_WLAN_USB + u32 packet_id; + u8 queueId; + struct wsm_tx *wsm_info; + struct bes2600_queue *queue; +#endif + + wsm_alloc_tx_buffer(hw_priv); + ret = wsm_get_tx(hw_priv, &data, &tx_len, tx_burst, &vif_selected); + if (ret <= 0) { + wsm_release_tx_buffer(hw_priv, 1); + if (WARN_ON(ret < 0)) { + bes2600_err(BES2600_DBG_BH, "bh get tx failed.\n"); + return ret; /* Error */ + } + return 0; /* No work */ + } + + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le16_to_cpu(wsm->len) != tx_len); +#ifdef BES2600_HOST_TIMESTAMP_DEBUG + tx_len += 4; +#endif + + atomic_add(1, &hw_priv->bh_tx); + + tx_len = hw_priv->sbus_ops->align_size( + hw_priv->sbus_priv, tx_len); + + /* Check if not exceeding BES2600 capabilities */ + if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) + bes2600_err(BES2600_DBG_BH, "Write aligned len: %zu\n", tx_len); + + wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= __cpu_to_le16(WSM_TX_SEQ(hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)])); + + //bes2600_dbg(BES2600_DBG_BH, "usb send buff len:%u.priv->hw_bufs_used:%d.\n", tx_len, priv->hw_bufs_used); + bes2600_dbg(BES2600_DBG_BH, "%s id:0x%04x seq:%d\n", __func__, + wsm->id, hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)]); +#ifdef CONFIG_BES2600_WLAN_USB + ret = hw_priv->sbus_ops->pipe_send(hw_priv->sbus_priv, 1, tx_len, data); + #if 0 + int count = 0; + while(priv->hw_bufs_used > 1) + { + mdelay(1); + count++; + if(count>2) + break; + } + #endif + if(ret < 0) { + /* requeue packet when send fail */ + wsm_info = (struct wsm_tx *)data; + if (data == hw_priv->wsm_cmd.ptr) { + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.ret = 1; + hw_priv->wsm_cmd.done = 1; + hw_priv->wsm_cmd.arg = NULL; + hw_priv->wsm_cmd.ptr = NULL; + spin_unlock(&hw_priv->wsm_cmd.lock); + wake_up(&hw_priv->wsm_cmd_wq); + } else { + packet_id = __le32_to_cpu(wsm_info->packetID); + queueId = bes2600_queue_get_queue_id(packet_id); + queue = &hw_priv->tx_queue[queueId]; +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_requeue(hw_priv, queue, packet_id, false); +#else + bes2600_queue_requeue(queue, packet_id, false); +#endif + } + wsm_release_tx_buffer(hw_priv, 1); + return 0; + } else if (vif_selected != -1) { + hw_priv->hw_bufs_used_vif[vif_selected]++; + } +#else +#ifndef BES_SDIO_TX_MULTIPLE_ENABLE + if (WARN_ON(bes2600_data_write(data, tx_len))) { +#else + if (WARN_ON(hw_priv->sbus_ops->pipe_send(hw_priv->sbus_priv, 1, tx_len, data))) { +#endif + bes2600_err(BES2600_DBG_BH, "tx blew up, len %zu\n", tx_len); + wsm_release_tx_buffer(hw_priv, 1); + return -1; /* Error */ + } + + if (vif_selected != -1) + hw_priv->hw_bufs_used_vif[vif_selected] ++; +#endif + + if (hw_priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("--> ", + DUMP_PREFIX_NONE, + data, + __le16_to_cpu(wsm->len)); + + wsm_txed(hw_priv, data); + + hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] = + (hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] + 1) & WSM_TX_SEQ_MAX; + bes2600_bh_inc_pending_count(hw_priv, WSM_TXRX_SEQ_IDX(wsm->id)); + + if (*tx_burst > 1) { + bes2600_debug_tx_burst(hw_priv); + return 1; /* Work remains */ + } + + return 0; +} + +#ifdef KEY_FRAME_SW_RETRY + +static inline bool +ieee80211_is_tcp_pkt(struct sk_buff *skb) +{ + + if (!skb) { + return false; + } + + if (skb->protocol == cpu_to_be16(ETH_P_IP)) { + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + if (iph->protocol == IPPROTO_TCP) { // TCP + bes2600_dbg(BES2600_DBG_BH, "################ %s line =%d.\n",__func__,__LINE__); + return true; + } + } + return false; +} + + +static int bes2600_need_retry_type(struct sk_buff *skb, int status) +{ + int ret = 0; + if (!skb) { + bes2600_info(BES2600_DBG_BH, "################ %s line =%d.\n",__func__,__LINE__); + return -1; + } + + if (skb->protocol == cpu_to_be16(ETH_P_IP)) { + if (ieee80211_is_tcp_pkt(skb)) { + ret = 1; + } + } + if (status != WSM_STATUS_RETRY_EXCEEDED) + ret = 0; + return ret; +} + +int bes2600_bh_sw_process(struct bes2600_common *hw_priv, + struct wsm_tx_confirm *tx_confirm) +{ + struct bes2600_txpriv *txpriv; + struct sk_buff *skb = NULL; + unsigned long timestamp = 0; + struct bes2600_queue *queue; + u8 queue_id, queue_gen; + +#ifdef KEY_FRAME_SW_RETRY + long delta_time; +#endif + if (!tx_confirm) { + bes2600_err(BES2600_DBG_BH, "%s tx_confirm is NULL\n", __func__); + return 0; + } + queue_id = bes2600_queue_get_queue_id(tx_confirm->packetID); + queue = &hw_priv->tx_queue[queue_id]; + + if (!queue) { + bes2600_err(BES2600_DBG_BH, "%s queue is NULL\n", __func__); + return 0; + } + + /* don't retry if the connection is already disconnected */ + queue_gen = bes2600_queue_get_generation(tx_confirm->packetID); + if(queue_gen != queue->generation) + return -1; + + bes2600_queue_get_skb_and_timestamp(queue, tx_confirm->packetID, + &skb, &txpriv, ×tamp); + if (skb == NULL) { + bes2600_err(BES2600_DBG_BH, "%s skb is NULL\n", __func__); + return -1; + } + if (timestamp > jiffies) + delta_time = jiffies + ((unsigned long)0xffffffff - timestamp); + else + delta_time = jiffies - timestamp; + bes2600_add_tx_delta_time(delta_time); + bes2600_add_tx_ac_delta_time(queue_id, delta_time); + + if (bes2600_need_retry_type(skb, tx_confirm->status) == 0) + return -1; + + if (delta_time > 1000) + return -1; + + if (txpriv->retry_count < CW1200_MAX_SW_RETRY_CNT ) { + struct bes2600_vif *priv = + __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + txpriv->retry_count++; + + bes2600_tx_status(priv,skb); + + bes2600_pwr_set_busy_event_with_timeout_async( + hw_priv, BES_PWR_LOCK_ON_TX, BES_PWR_EVENT_TX_TIMEOUT); + + bes2600_sw_retry_requeue(hw_priv, queue, tx_confirm->packetID, true); + return 0; + } else { + txpriv->retry_count = 0; + } + + return -1; +} +#endif + +void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx) +{ + struct timer_list *timer = (idx == 0) ? &hw_priv->lmac_mon_timer + : &hw_priv->mcu_mon_timer; + + if(hw_priv->wsm_tx_pending[idx]++ == 0) { + bes2600_dbg(BES2600_DBG_BH, "start timer in tx, idx:%d\n", idx); + mod_timer(timer, jiffies + 3 * HZ); + } +} + +void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx) +{ + struct timer_list *timer = (idx == 0) ? &hw_priv->lmac_mon_timer + : &hw_priv->mcu_mon_timer; + + if(hw_priv->wsm_tx_pending[idx] == 0) { + bes2600_err(BES2600_DBG_TXRX, "tx pending count error, idx:%d\n", idx); + return; + } + + if(--hw_priv->wsm_tx_pending[idx] == 0) { + del_timer_sync(timer); + } else { + mod_timer(timer, jiffies + 3 * HZ); + } +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) +void bes2600_bh_mcu_active_monitor(struct timer_list* t) +{ + struct bes2600_common *hw_priv = from_timer(hw_priv, t, mcu_mon_timer); + + bes2600_err(BES2600_DBG_TXRX, "link break between mcu and host, hw_buf_used:%d pending:%d\n", + hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[1]); + bes2600_chrdev_wifi_force_close(hw_priv, true); +} +#else +void bes2600_bh_mcu_active_monitor(unsigned long data) +{ + struct bes2600_common *hw_priv = (struct bes2600_common *)data; + + bes2600_err(BES2600_DBG_TXRX, "link break between mcu and host, hw_buf_used:%d pending:%d\n", + hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[1]); + bes2600_chrdev_wifi_force_close(hw_priv, true); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) +void bes2600_bh_lmac_active_monitor(struct timer_list* t) +{ + struct bes2600_common *hw_priv = from_timer(hw_priv, t, lmac_mon_timer); + + bes2600_err(BES2600_DBG_TXRX, "link break between lmac and host, hw_buf_used:%d pending:%d\n", + hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[0]); + bes2600_chrdev_wifi_force_close(hw_priv, true); +} +#else +void bes2600_bh_lmac_active_monitor(unsigned long data) +{ + struct bes2600_common *hw_priv = (struct bes2600_common *)data; + + bes2600_err(BES2600_DBG_TXRX, "link break between lmac and host, hw_buf_used:%d pending:%d\n", + hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[0]); + bes2600_chrdev_wifi_force_close(hw_priv, true); +} +#endif + + +#define BH_RX_CONT_LIMIT 3 +#define BH_TX_CONT_LIMIT 20 +static int bes2600_bh(void *arg) +{ + struct bes2600_common *hw_priv = arg; + int rx, tx, term, suspend; + int tx_allowed; + int pending_tx = 0; + int tx_burst; + long status; + int ret; + + int tx_cont = 0; + int rx_cont = 0; + + for (;;) { + rx_cont = 0; + tx_cont = 0; + + if (!hw_priv->hw_bufs_used && + !bes2600_pwr_device_is_idle(hw_priv) && + !atomic_read(&hw_priv->recent_scan)) { + status = 5 * HZ; + } else if (hw_priv->hw_bufs_used) { + /* Interrupt loss detection */ + status = 5 * HZ; + } else { + status = MAX_SCHEDULE_TIMEOUT; + } + + status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({ + rx = atomic_xchg(&hw_priv->bh_rx, 0); + tx = atomic_xchg(&hw_priv->bh_tx, 0); + term = atomic_xchg(&hw_priv->bh_term, 0); + suspend = pending_tx ? + 0 : atomic_read(&hw_priv->bh_suspend); + (rx || tx || term || suspend || hw_priv->bh_error); + }), status); + + //bes2600_err(BES2600_DBG_BH, "[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, bufused: %d, status: %ld\n", + //rx, tx, term, suspend, hw_priv->bh_error, hw_priv->hw_bufs_used, status); + + /* Did an error occur? */ + if ((status < 0 && status != -ERESTARTSYS) || + term || hw_priv->bh_error) { + break; + } + if (!status) { /* wait_event timed out */ + #ifdef CONFIG_BES2600_WLAN_BES + unsigned long timestamp = jiffies; + long timeout; + int pending = 0; + int i; + #endif + /* Check to see if we have any outstanding frames */ + if (hw_priv->hw_bufs_used && (!rx || !tx)) { + bes2600_err(BES2600_DBG_BH, "usedbuf:%u. rx:%u. tx:%u.\n", hw_priv->hw_bufs_used, rx, tx); + #ifdef CONFIG_BES2600_WLAN_SDIO + sdio_work_debug(hw_priv->sbus_priv); + #endif + #ifdef CONFIG_BES2600_WLAN_BES + bes2600_err(BES2600_DBG_BH, "Missed interrupt? (%d frames outstanding)\n", + hw_priv->hw_bufs_used); + rx = 1; + + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending += bes2600_queue_get_xmit_timestamp( + &hw_priv->tx_queue[i], + ×tamp, i, + hw_priv->pending_frame_id); + + /* Check if frame transmission is timed out. + * Add an extra second with respect to possible + * interrupt loss. + */ + timeout = timestamp + + WSM_CMD_LAST_CHANCE_TIMEOUT + + 1 * HZ - + jiffies; + + /* And terminate BH thread if the frame is "stuck" */ + if (pending && timeout < 0) { + wiphy_warn(hw_priv->hw->wiphy, + "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n", + hw_priv->hw_bufs_used, pending, + timestamp, jiffies); + } + #endif + + bes2600_chrdev_wifi_force_close(hw_priv, false); + } +#ifdef BES2600_RX_IN_BH + goto rx; +#else + goto done; +#endif + } else if (suspend) { + bes2600_dbg(BES2600_DBG_BH, "[BH] Device suspend.\n"); + + atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED); + wake_up(&hw_priv->bh_evt_wq); + status = wait_event_interruptible(hw_priv->bh_wq, + BES2600_BH_RESUME == atomic_read(&hw_priv->bh_suspend)); + if (status < 0) { + wiphy_err(hw_priv->hw->wiphy, + "Failed to wait for resume: %ld.\n", + status); + break; + } + bes2600_dbg(BES2600_DBG_BH, "[BH] Device resume.\n"); + atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); + wake_up(&hw_priv->bh_evt_wq); + atomic_add(1, &hw_priv->bh_rx); + goto done; + } + + rx: + tx += pending_tx; + pending_tx = 0; +#ifdef BES2600_RX_IN_BH +#ifdef CONFIG_BES2600_WLAN_SPI + if (rx) { +#endif + ret = bes2600_bh_rx_helper(hw_priv, &tx); + if (ret < 0) { + bes2600_err(BES2600_DBG_BH, "bes2600_bh_rx_helper fail\n"); + #ifdef CONFIG_BES2600_WLAN_SDIO + sdio_work_debug(hw_priv->sbus_priv); + #endif + // break; // rx error + bes2600_chrdev_wifi_force_close(hw_priv, false); + } + else if (ret == 1) { + rx = 1; // continue rx + rx_cont++; + } + else + rx = 0; // wait for a new rx event + if (rx && (rx_cont < BH_RX_CONT_LIMIT)) + goto rx; + rx_cont = 0; +#ifdef CONFIG_BES2600_WLAN_SPI + } +#endif +#endif + tx: + if (1) { + + tx = 0; + + BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); + tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used; + tx_allowed = tx_burst > 0; + + if (!tx_allowed) { + /* Buffers full. Ensure we process tx + * after we handle rx.. + */ + #ifndef CONFIG_BES2600_WLAN_SDIO + bes2600_err(BES2600_DBG_BH, "bh tx not allowed.\n"); + #endif + pending_tx = tx; + goto done_rx; + } + ret = bes2600_bh_tx_helper(hw_priv, &pending_tx, &tx_burst); + if (ret < 0) { + bes2600_err(BES2600_DBG_BH, "bes2600_bh_tx_helper fail\n"); + #ifdef CONFIG_BES2600_WLAN_SDIO + sdio_work_debug(hw_priv->sbus_priv); + #endif + break; + } + if (ret > 0) { + /* More to transmit */ + tx_cont++; + tx = ret; + } + + if (tx && tx_cont < BH_TX_CONT_LIMIT) + goto tx; + tx_cont = 0; +#if 0 + /* Re-read ctrl reg */ + if (bes2600_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; +#endif + } + + done_rx: + if (hw_priv->bh_error) + break; + //if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + if (rx) + goto rx; + if (tx) + goto tx; + + done: + /* Re-enable device interrupts */ + //hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + //__bes2600_irq_enable(1); + //hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + asm volatile ("nop"); + } + + /* Explicitly disable device interrupts */ + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + __bes2600_irq_enable(0); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + + if (!term) { + bes2600_err(BES2600_DBG_BH, "[BH] Fatal error, exiting.\n"); + #ifdef CONFIG_BES2600_WLAN_SDIO + sdio_work_debug(hw_priv->sbus_priv); + #endif + hw_priv->bh_error = 1; + /* TODO: schedule_work(recovery) */ + } + return 0; +} +#endif diff --git a/drivers/staging/bes2600/bh.h b/drivers/staging/bes2600/bh.h new file mode 100644 index 000000000000..d43f758e53a0 --- /dev/null +++ b/drivers/staging/bes2600/bh.h @@ -0,0 +1,53 @@ +/* + * Device handling thread interface for mac80211 BES2600 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_BH_H +#define BES2600_BH_H + +#include + +/* extern */ struct bes2600_common; + +#define SDIO_BLOCK_SIZE (528) + +#define KEY_FRAME_SW_RETRY +#ifdef KEY_FRAME_SW_RETRY +#define CW1200_MAX_SW_RETRY_CNT (2) +#endif + + +int bes2600_register_bh(struct bes2600_common *hw_priv); +void bes2600_unregister_bh(struct bes2600_common *hw_priv); +void bes2600_irq_handler(struct bes2600_common *hw_priv); +void bes2600_bh_wakeup(struct bes2600_common *hw_priv); +int bes2600_bh_suspend(struct bes2600_common *hw_priv); +int bes2600_bh_resume(struct bes2600_common *hw_priv); +/* Must be called from BH thread. */ +void bes2600_enable_powersave(struct bes2600_vif *priv, + bool enable); +int wsm_release_tx_buffer(struct bes2600_common *hw_priv, int count); +int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id, + int count); +int bes2600_bh_sw_process(struct bes2600_common *hw_priv, + struct wsm_tx_confirm *tx_confirm); + +void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx); +void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) +void bes2600_bh_mcu_active_monitor(struct timer_list* t); +void bes2600_bh_lmac_active_monitor(struct timer_list* t); +#else +void bes2600_bh_mcu_active_monitor(unsigned long data); +void bes2600_bh_lmac_active_monitor(unsigned long data); +#endif + +#endif /* BES2600_BH_H */ diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c new file mode 100644 index 000000000000..c5bba0789a3c --- /dev/null +++ b/drivers/staging/bes2600/debug.c @@ -0,0 +1,784 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/*Linux version 3.4.0 compilation*/ +//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +#include +//#endif +#include +#include +#include "bes2600.h" +#include "debug.h" +#ifdef CONFIG_BES2600_DEBUGFS +/* join_status */ +static const char * const bes2600_debug_join_status[] = { + "passive", + "monitor", + "station", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const bes2600_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + +static const char * const bes2600_debug_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test", +}; + +static const char * const bes2600_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", +}; + +static const char *bes2600_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "ad-hok"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void bes2600_queue_status_show(struct seq_file *seq, + struct bes2600_queue *q) +{ + int i, if_id; + seq_printf(seq, "Queue %d:\n", (int)q->queue_id); + seq_printf(seq, " capacity: %ld\n", (long)q->capacity); + seq_printf(seq, " queued: %ld\n", (long)q->num_queued); + seq_printf(seq, " pending: %ld\n", (long)q->num_pending); + seq_printf(seq, " sent: %ld\n", (long)q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (if_id = 0; if_id < CW12XX_MAX_VIFS; if_id++) { + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[if_id][i]); + seq_printf(seq, "<-%ld\n", (long)q->stats->map_capacity); + } +} + +static void bes2600_debug_print_map(struct seq_file *seq, + struct bes2600_vif *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->hw_priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%ld\n", + (long)priv->hw_priv->tx_queue_stats.map_capacity - 1); +} + +static int bes2600_status_show_common(struct seq_file *seq, void *v) +{ + int i; + struct list_head *item; + struct bes2600_common *hw_priv = seq->private; + struct bes2600_debug_common *d = hw_priv->debug; + int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0; + bool ba_ena; + + spin_lock_bh(&hw_priv->ba_lock); + ba_cnt = hw_priv->debug->ba_cnt; + ba_acc = hw_priv->debug->ba_acc; + ba_cnt_rx = hw_priv->debug->ba_cnt_rx; + ba_acc_rx = hw_priv->debug->ba_acc_rx; + ba_ena = hw_priv->ba_ena; + if (ba_cnt) + ba_avg = ba_acc / ba_cnt; + if (ba_cnt_rx) + ba_avg_rx = ba_acc_rx / ba_cnt_rx; + spin_unlock_bh(&hw_priv->ba_lock); + + seq_puts(seq, "BES2600 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + hw_priv->wsm_caps.hardwareId, + hw_priv->wsm_caps.hardwareSubId); + seq_printf(seq, "Firmware: %s %d.%d\n", + bes2600_debug_fw_types[hw_priv->wsm_caps.firmwareType], + hw_priv->wsm_caps.firmwareVersion, + hw_priv->wsm_caps.firmwareBuildNumber); + seq_printf(seq, "FW API: %d\n", + hw_priv->wsm_caps.firmwareApiVer); + seq_printf(seq, "FW caps: 0x%.4X\n", + hw_priv->wsm_caps.firmwareCap); + if (hw_priv->channel) + seq_printf(seq, "Channel: %d%s\n", + hw_priv->channel->hw_value, + hw_priv->channel_switch_in_progress ? + " (switching)" : ""); + seq_printf(seq, "HT: %s\n", + bes2600_is_ht(&hw_priv->ht_info) ? "on" : "off"); + if (bes2600_is_ht(&hw_priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + bes2600_ht_greenfield(&hw_priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + bes2600_ht_ampdu_density(&hw_priv->ht_info)); + } + spin_lock_bh(&hw_priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &hw_priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&hw_priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); + seq_printf(seq, "BA stat: %d, %d (%d)\n", + ba_cnt, ba_acc, ba_avg); + seq_printf(seq, "BA RX stat: %d, %d (%d)\n", + ba_cnt_rx, ba_acc_rx, ba_avg_rx); + seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off"); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + bes2600_queue_status_show(seq, &hw_priv->tx_queue[i]); + seq_puts(seq, "\n"); + } + seq_printf(seq, "TX burst: %d\n", + d->tx_burst); + seq_printf(seq, "RX burst: %d\n", + d->rx_burst); + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "Long retr: %d\n", + hw_priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + hw_priv->short_frame_max_tx_count); + + seq_printf(seq, "BH status: %s\n", + atomic_read(&hw_priv->bh_term) ? "terminated" : "alive"); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&hw_priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&hw_priv->bh_tx)); + if (hw_priv->bh_error) + seq_printf(seq, "BH errcode: %d\n", + hw_priv->bh_error); + seq_printf(seq, "TX bufs: %d x %d bytes\n", + hw_priv->wsm_caps.numInpChBufs, + hw_priv->wsm_caps.sizeInpChBuf); + seq_printf(seq, "Used bufs: %d\n", + hw_priv->hw_bufs_used); + seq_printf(seq, "Device: %s\n", + bes2600_pwr_device_is_idle(hw_priv) ? "alseep" : "awake"); + + spin_lock(&hw_priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + hw_priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%ld bytes)\n", + hw_priv->wsm_cmd.cmd, (long)hw_priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + hw_priv->wsm_cmd.ret); + spin_unlock(&hw_priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&hw_priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&hw_priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&hw_priv->tx_lock)); + + seq_printf(seq, "Scan: %s\n", + atomic_read(&hw_priv->scan.in_progress) ? "active" : "idle"); + seq_printf(seq, "Led state: 0x%.2X\n", + hw_priv->softled_state); + + return 0; +} + +static int bes2600_status_open_common(struct inode *inode, struct file *file) +{ + return single_open(file, &bes2600_status_show_common, + inode->i_private); +} + +static const struct file_operations fops_status_common = { + .open = bes2600_status_open_common, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int bes2600_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct bes2600_common *hw_priv = seq->private; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(hw_priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.CAT_STR(count, name))) + + PUT_COUNTER("\t\t", PlcpErrors); + PUT_COUNTER("\t\t", FcsErrors); + PUT_COUNTER("\t\t", TxPackets); + PUT_COUNTER("\t\t", RxPackets); + PUT_COUNTER("\t\t", RxPacketErrors); + PUT_COUNTER("\t", RxDecryptionFailures); + PUT_COUNTER("\t\t", RxMicFailures); + PUT_COUNTER("\t", RxNoKeyFailures); + PUT_COUNTER("\t", TxMulticastFrames); + PUT_COUNTER("\t", TxFramesSuccess); + PUT_COUNTER("\t", TxFrameFailures); + PUT_COUNTER("\t", TxFramesRetried); + PUT_COUNTER("\t", TxFramesMultiRetried); + PUT_COUNTER("\t", RxFrameDuplicates); + PUT_COUNTER("\t\t", RtsSuccess); + PUT_COUNTER("\t\t", RtsFailures); + PUT_COUNTER("\t\t", AckFailures); + PUT_COUNTER("\t", RxMulticastFrames); + PUT_COUNTER("\t", RxFramesSuccess); + PUT_COUNTER("\t", RxCMACICVErrors); + PUT_COUNTER("\t\t", RxCMACReplays); + PUT_COUNTER("\t", RxMgmtCCMPReplays); + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int bes2600_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &bes2600_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = bes2600_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int bes2600_power_busy_event_show(struct seq_file *seq, void *v) +{ + struct bes2600_common *hw_priv = seq->private; + char *buffer = NULL; + + buffer = kmalloc(8192, GFP_KERNEL); + if(!buffer) + return -ENOMEM; + + if(bes2600_pwr_busy_event_dump(hw_priv, buffer, 8192) == 0) { + seq_printf(seq, "%s", buffer); + } else { + return -EFBIG; + } + + kfree(buffer); + + return 0; +} + +static int bes2600_power_busy_open(struct inode *inode, struct file *file) +{ + return single_open(file, &bes2600_power_busy_event_show, + inode->i_private); +} + +static const struct file_operations fops_power_busy_events = { + .open = bes2600_power_busy_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int bes2600_generic_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t bes2600_11n_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *hw_priv = file->private_data; + struct ieee80211_supported_band *band = + hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ]; + return simple_read_from_buffer(user_buf, count, ppos, + band->ht_cap.ht_supported ? "1\n" : "0\n", 2); +} + +static ssize_t bes2600_11n_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *hw_priv = file->private_data; + struct ieee80211_supported_band *band[2] = { + hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ], + hw_priv->hw->wiphy->bands[NL80211_BAND_5GHZ], + }; + char buf[1]; + int ena = 0; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + if (buf[0] == 1) + ena = 1; + + band[0]->ht_cap.ht_supported = ena; +#ifdef CONFIG_BES2600_5GHZ_SUPPORT + band[1]->ht_cap.ht_supported = ena; +#endif /* CONFIG_BES2600_5GHZ_SUPPORT */ + + return count; +} + +static const struct file_operations fops_bes2600_log_control = { + .open = bes2600_log_control_open, + .read = bes2600_log_control_read, + .write = bes2600_log_control_write, + .llseek = default_llseek, +}; + +static const struct file_operations fops_11n = { + .open = bes2600_generic_open, + .read = bes2600_11n_read, + .write = bes2600_11n_write, + .llseek = default_llseek, +}; + +static ssize_t bes2600_wsm_dumps(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *hw_priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (buf[0] == '1') + hw_priv->wsm_enable_wsm_dumps = 1; + else + hw_priv->wsm_enable_wsm_dumps = 0; + + return count; +} + +static const struct file_operations fops_wsm_dumps = { + .open = bes2600_generic_open, + .write = bes2600_wsm_dumps, + .llseek = default_llseek, +}; + +#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT) +static ssize_t bes2600_short_dump_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *hw_priv = file->private_data; + char buf[20]; + size_t size = 0; + + sprintf(buf, "Size: %u\n", hw_priv->wsm_dump_max_size); + size = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, + buf, size); +} + +static ssize_t bes2600_short_dump_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *priv = file->private_data; + char buf[20]; + unsigned long dump_size = 0; + + if (!count || count > 20) + return -EINVAL; + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (kstrtoul(buf, 10, &dump_size)) + return -EINVAL; + + priv->wsm_dump_max_size = dump_size; + + return count; +} + +static const struct file_operations fops_short_dump = { + .open = bes2600_generic_open, + .write = bes2600_short_dump_write, + .read = bes2600_short_dump_read, + .llseek = default_llseek, +}; +#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */ + +#ifdef BES2600_DUMP_FW_DPD_LOG +extern void bes2600_get_dpd_log(char **data, size_t *len); +static ssize_t bes2600_dpd_log_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char empty[] = {'\n', '\0'}; + char *data; + size_t size; + + bes2600_get_dpd_log(&data, &size); + if (!data) { + data = empty; + size = sizeof(empty); + } + + return simple_read_from_buffer(user_buf, count, ppos, + data, size); +} + +static const struct file_operations dpd_log_dump = { + .open = bes2600_generic_open, + .read = bes2600_dpd_log_read, + .llseek = default_llseek, +}; +#endif /* BES2600_DUMP_FW_DPD_LOG */ + +int bes2600_debug_init_common(struct bes2600_common *hw_priv) +{ + int ret = -ENOMEM; + struct bes2600_debug_common *d = + kzalloc(sizeof(struct bes2600_debug_common), GFP_KERNEL); + hw_priv->debug = d; + if (!d) + return ret; + + d->debugfs_phy = debugfs_create_dir("bes2600", + hw_priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + goto err; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_status_common)) + goto err; + + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_counters)) + goto err; + + if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR, + d->debugfs_phy, hw_priv, &fops_11n)) + goto err; + + if (!debugfs_create_file("bes2600_log_control", S_IRUSR | S_IWUSR, + d->debugfs_phy, hw_priv, &fops_bes2600_log_control)) + goto err; + + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + hw_priv, &fops_wsm_dumps)) + goto err; + +#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT) + if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR, + d->debugfs_phy, hw_priv, &fops_short_dump)) + goto err; +#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */ + +#ifdef CONFIG_BES2600_WOWLAN + if (!debugfs_create_file("power_events", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_power_busy_events)) + goto err; +#endif + +#ifdef BES2600_DUMP_FW_DPD_LOG + if (!debugfs_create_file("dpd_log", S_IRUSR, d->debugfs_phy, + hw_priv, &dpd_log_dump)) + goto err; +#endif + + ret = bes2600_itp_init(hw_priv); + if (ret) + goto err; + + return 0; + +err: + hw_priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; +} + +void bes2600_debug_release_common(struct bes2600_common *hw_priv) +{ + struct bes2600_debug_common *d = hw_priv->debug; + if (d) { + bes2600_itp_release(hw_priv); + hw_priv->debug = NULL; + kfree(d); + } +} + +static int bes2600_status_show_priv(struct seq_file *seq, void *v) +{ + int i; + struct bes2600_vif *priv = seq->private; + struct bes2600_debug_priv *d = priv->debug; + + seq_printf(seq, "Mode: %s%s\n", + bes2600_debug_mode(priv->mode), + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Assoc: %s\n", + bes2600_debug_join_status[priv->join_status]); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) + seq_puts(seq, "Filter: fcs\n"); + if (priv->rx_filter.bssid) + seq_puts(seq, "Filter: bssid\n"); + if (priv->bf_control.bcn_count) + seq_puts(seq, "Filter: beacons\n"); + + if (priv->enable_beacon || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "Beaconing: %s\n", + priv->enable_beacon ? + "enabled" : "disabled"); + if (priv->ssid_length || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "SSID: %.*s\n", + (int)priv->ssid_length, priv->ssid); + + for (i = 0; i < 4; ++i) { + seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, + priv->edca.params[i].cwMin, + priv->edca.params[i].cwMax, + priv->edca.params[i].aifns, + priv->edca.params[i].txOpLimit, + priv->edca.params[i].maxReceiveLifetime); + } + if (priv->join_status == BES2600_JOIN_STATUS_STA) { + static const char *pmMode = "unknown"; + switch (priv->powersave_mode.pmMode) { + case WSM_PSM_ACTIVE: + pmMode = "off"; + break; + case WSM_PSM_PS: + pmMode = "on"; + break; + case WSM_PSM_FAST_PS: + pmMode = "dynamic"; + break; + } + seq_printf(seq, "Preamble: %s\n", + bes2600_debug_preamble[ + priv->association_mode.preambleType]); + seq_printf(seq, "AMPDU spcn: %d\n", + priv->association_mode.mpduStartSpacing); + seq_printf(seq, "Basic rate: 0x%.8X\n", + le32_to_cpu(priv->association_mode.basicRateSet)); + seq_printf(seq, "Bss lost: %d beacons\n", + priv->bss_params.beaconLostCount); + seq_printf(seq, "AID: %d\n", + priv->bss_params.aid); + seq_printf(seq, "Rates: 0x%.8X\n", + priv->bss_params.operationalRateSet); + seq_printf(seq, "Powersave: %s\n", pmMode); + } + seq_printf(seq, "RSSI thold: %d\n", + priv->cqm_rssi_thold); + seq_printf(seq, "RSSI hyst: %d\n", + priv->cqm_rssi_hyst); + seq_printf(seq, "TXFL thold: %d\n", + priv->cqm_tx_failure_thold); + seq_printf(seq, "Linkloss: %d\n", + priv->cqm_link_loss_count); + seq_printf(seq, "Bcnloss: %d\n", + priv->cqm_beacon_loss_count); + + bes2600_debug_print_map(seq, priv, "Link map: ", + priv->link_id_map); + bes2600_debug_print_map(seq, priv, "Asleep map: ", + priv->sta_asleep_mask); + bes2600_debug_print_map(seq, priv, "PSPOLL map: ", + priv->pspoll_mask); + + seq_puts(seq, "\n"); + + for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) { + if (priv->link_id_db[i].status) { + seq_printf(seq, "Link %d: %s, %pM\n", + i + 1, bes2600_debug_link_id[ + priv->link_id_db[i].status], + priv->link_id_db[i].mac); + } + } + + seq_puts(seq, "\n"); + + seq_printf(seq, "Powermgmt: %s\n", + priv->powersave_enabled ? "on" : "off"); + + seq_printf(seq, "TXed: %d\n", + d->tx); + seq_printf(seq, "AGG TXed: %d\n", + d->tx_agg); + seq_printf(seq, "MULTI TXed: %d (%d)\n", + d->tx_multi, d->tx_multi_frames); + seq_printf(seq, "RXed: %d\n", + d->rx); + seq_printf(seq, "AGG RXed: %d\n", + d->rx_agg); + seq_printf(seq, "TX align: %d\n", + d->tx_align); + seq_printf(seq, "TX TTL: %d\n", + d->tx_ttl); + return 0; +} + +static int bes2600_status_open_priv(struct inode *inode, struct file *file) +{ + return single_open(file, &bes2600_status_show_priv, + inode->i_private); +} + +static const struct file_operations fops_status_priv = { + .open = bes2600_status_open_priv, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + +static ssize_t bes2600_hang_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_vif *priv = file->private_data; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (priv->vif) { + bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ); + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + } else + return -ENODEV; + + return count; +} + +static const struct file_operations fops_hang = { + .open = bes2600_generic_open, + .write = bes2600_hang_write, + .llseek = default_llseek, +}; +#endif + +#define VIF_DEBUGFS_NAME_S 10 +int bes2600_debug_init_priv(struct bes2600_common *hw_priv, + struct bes2600_vif *priv) +{ + int ret = -ENOMEM; + struct bes2600_debug_priv *d; + char name[VIF_DEBUGFS_NAME_S]; + + if (WARN_ON(!hw_priv)) + return ret; + + if (WARN_ON(!hw_priv->debug)) + return ret; + + d = kzalloc(sizeof(struct bes2600_debug_priv), GFP_KERNEL); + priv->debug = d; + if (WARN_ON(!d)) + return ret; + + memset(name, 0, VIF_DEBUGFS_NAME_S); + ret = snprintf(name, VIF_DEBUGFS_NAME_S, "vif_%d", priv->if_id); + if (WARN_ON(ret < 0)) + goto err; + + d->debugfs_phy = debugfs_create_dir(name, + hw_priv->debug->debugfs_phy); + if (WARN_ON(!d->debugfs_phy)) + goto err; + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + if (WARN_ON(!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy, + priv, &fops_hang))) + goto err; +#endif + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status_priv)) + goto err; + + return 0; +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; + +} + +void bes2600_debug_release_priv(struct bes2600_vif *priv) +{ + struct bes2600_debug_priv *d = priv->debug; + if (d) { + debugfs_remove_recursive(priv->debug->debugfs_phy); + priv->debug = NULL; + kfree(d); + } +} + +int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len) +{ + return snprintf(buf, len, "%s %d.%d", + bes2600_debug_fw_types[hw_priv->wsm_caps.firmwareType], + hw_priv->wsm_caps.firmwareVersion, + hw_priv->wsm_caps.firmwareBuildNumber); +} +#endif diff --git a/drivers/staging/bes2600/debug.h b/drivers/staging/bes2600/debug.h new file mode 100644 index 000000000000..371457755669 --- /dev/null +++ b/drivers/staging/bes2600/debug.h @@ -0,0 +1,191 @@ +/* + * DebugFS code for BES2600 mac80211 driver + * + * Copyright (c) 2011, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_DEBUG_H_INCLUDED +#define BES2600_DEBUG_H_INCLUDED + +#include "itp.h" + +struct cw200_common; + +struct bes2600_debug_common { + struct dentry *debugfs_phy; + int tx_cache_miss; + int tx_burst; + int rx_burst; + int ba_cnt; + int ba_acc; + int ba_cnt_rx; + int ba_acc_rx; +#ifdef CONFIG_BES2600_ITP + struct bes2600_itp itp; +#endif /* CONFIG_BES2600_ITP */ +}; + +struct bes2600_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int rx; + int rx_agg; + int tx_multi; + int tx_multi_frames; + int tx_align; + int tx_ttl; +}; + +#ifdef CONFIG_BES2600_DEBUGFS +int bes2600_debug_init_common(struct bes2600_common *hw_priv); +int bes2600_debug_init_priv(struct bes2600_common *hw_priv, + struct bes2600_vif *priv); +void bes2600_debug_release_common(struct bes2600_common *hw_priv); +void bes2600_debug_release_priv(struct bes2600_vif *priv); + +static inline void bes2600_debug_txed(struct bes2600_vif *priv) +{ + ++priv->debug->tx; +} + +static inline void bes2600_debug_txed_agg(struct bes2600_vif *priv) +{ + ++priv->debug->tx_agg; +} + +static inline void bes2600_debug_txed_multi(struct bes2600_vif *priv, + int count) +{ + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + +static inline void bes2600_debug_rxed(struct bes2600_vif *priv) +{ + ++priv->debug->rx; +} + +static inline void bes2600_debug_rxed_agg(struct bes2600_vif *priv) +{ + ++priv->debug->rx_agg; +} + +static inline void bes2600_debug_tx_cache_miss(struct bes2600_common *common) +{ + ++common->debug->tx_cache_miss; +} + +static inline void bes2600_debug_tx_align(struct bes2600_vif *priv) +{ + ++priv->debug->tx_align; +} + +static inline void bes2600_debug_tx_ttl(struct bes2600_vif *priv) +{ + ++priv->debug->tx_ttl; +} + +static inline void bes2600_debug_tx_burst(struct bes2600_common *hw_priv) +{ + ++hw_priv->debug->tx_burst; +} + +static inline void bes2600_debug_rx_burst(struct bes2600_common *hw_priv) +{ + ++hw_priv->debug->rx_burst; +} + +static inline void bes2600_debug_ba(struct bes2600_common *hw_priv, + int ba_cnt, int ba_acc, int ba_cnt_rx, + int ba_acc_rx) +{ + hw_priv->debug->ba_cnt = ba_cnt; + hw_priv->debug->ba_acc = ba_acc; + hw_priv->debug->ba_cnt_rx = ba_cnt_rx; + hw_priv->debug->ba_acc_rx = ba_acc_rx; +} + +int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len); + +#else /* CONFIG_BES2600_DEBUGFS */ + +static inline int bes2600_debug_init_common(struct bes2600_common *hw_priv) +{ + return 0; +} + +static inline int bes2600_debug_init_priv(struct bes2600_common *hw_priv, + struct bes2600_vif *priv) +{ + return 0; +} + +static inline void bes2600_debug_release_common(struct bes2600_common *hw_priv) +{ +} + +static inline void bes2600_debug_release_priv(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_txed(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_txed_agg(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_txed_multi(struct bes2600_vif *priv, + int count) +{ +} + +static inline void bes2600_debug_rxed(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_rxed_agg(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_tx_cache_miss(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_tx_align(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_tx_ttl(struct bes2600_vif *priv) +{ +} + +static inline void bes2600_debug_tx_burst(struct bes2600_common *hw_priv) +{ +} + +static inline void bes2600_debug_rx_burst(struct bes2600_common *hw_priv) +{ +} + +static inline void bes2600_debug_ba(struct bes2600_common *hw_priv, + int ba_cnt, int ba_acc, int ba_cnt_rx, + int ba_acc_rx) +{ +} + +static inline int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len) +{ + return 0; +} + +#endif /* CONFIG_BES2600_DEBUGFS */ + +#endif /* BES2600_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/bes2600/epta_coex.c b/drivers/staging/bes2600/epta_coex.c new file mode 100644 index 000000000000..25690f38e0a0 --- /dev/null +++ b/drivers/staging/bes2600/epta_coex.c @@ -0,0 +1,584 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include + +#include "bes2600.h" +#include "epta_coex.h" +#include "epta_request.h" + +static bool coex_ps_en; +static bool coex_fdd_mode; /* fdd or fdd hybrid */ +static uint8_t epta_conn_state; /* dafault invalid stat */ +static uint32_t epta_freeze_bitmap; /*bit0: conn, bit1: tp, bit2: tts */ +static int epta_freezed_wlan_duration[EPTA_FREEZE_MAX] = {0}; +static int epta_freezed_wlan_duration_cfg = 0; +static int wlan_duration_cfg; +static int bt_duration_cfg; +static int hw_epta_enable_cfg; /* bit0~bit1: epta mode, bit7: prot flag, other bits: reserved */ +static int epta_ps_mode; /* 0 - normal mode, 1 - power save mode */ +static int epta_adjust_cnt; +static int coex_bt_state; +static uint32_t g_coex_mode; + +#define EPTA_MODE_CFG (hw_epta_enable_cfg & 0x03) + +/* + * This function will send wsm command to set epta module with inputing parameters. + * The inputing parameters may not be used really if epta_freeze_bitmap is not zero. + * @hw_epta_enable: when hw_epta_enable is 1, use hardware pta, epta does not work, + wlan_duration and bt_duration mean nothing. + * @wlan_duration and bt_duration: need set hw_epta_enable to 0, use software epta. + Allow 10~100000 us. + * e.g. + * coex_set_epta_params(20000, 20000, 0); means use software epta, wlan get 20ms, bt get 20ms. + * coex_set_epta_params(20000, 20000, 1); means use hardware pta, hardware arbitrate. + */ +int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int hw_epta_enable) +{ + struct wsm_epta_msg msg; + + if (hw_priv == NULL) { + bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n"); + return -1; + } + + bes2600_dbg(BES2600_DBG_EPTA, "set epta w:%d hw:%d wc:%d fbit:%x fw:%d\n", wlan_duration, hw_epta_enable, + wlan_duration_cfg, epta_freeze_bitmap, epta_freezed_wlan_duration_cfg); + + if (wlan_duration_cfg == wlan_duration && bt_duration_cfg == bt_duration && hw_epta_enable_cfg == hw_epta_enable) { + bes2600_dbg(BES2600_DBG_EPTA, "same epta params\n"); + return 0; + } + + wlan_duration_cfg = wlan_duration; + bt_duration_cfg = bt_duration; + hw_epta_enable_cfg = hw_epta_enable; + + /* If in freeze mode, use freezed wlan duration; */ + if (epta_freeze_bitmap) { + if (epta_freeze_bitmap & EPTA_FREEZE_SCANNING) { + bes2600_info(BES2600_DBG_EPTA, "skip bt state in wifi scanning"); + return 0; + } + + /* + if new wlan duration is more than freezed one, update fw settings with new duration + if new wlan duration is less than freezed one, update fw settings with freezed duration + */ + if (wlan_duration < epta_freezed_wlan_duration_cfg) { + bes2600_info(BES2600_DBG_EPTA, "epta_freeze_bitmap:0x%x, wlan:%u < %u", + epta_freeze_bitmap, wlan_duration, epta_freezed_wlan_duration_cfg); + wlan_duration = epta_freezed_wlan_duration_cfg; + bt_duration = EPTA_FREEZE_TDD_PERIOD - epta_freezed_wlan_duration_cfg; + } + } + + /* epta disconnect status,force use HW epta */ + if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED && hw_epta_enable == 0) + hw_epta_enable = 1; + + msg.wlan_duration = wlan_duration; + msg.bt_duration = bt_duration; + msg.hw_epta_enable = hw_epta_enable; + + return wsm_epta_cmd(hw_priv, &msg); +} + +static int coex_epta_freeze_update(struct bes2600_common *hw_priv) +{ + struct wsm_epta_msg msg; + int i = 0; + int max_freeze = 0; + + if (hw_priv == NULL) { + bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n"); + return -1; + } + + bes2600_dbg(BES2600_DBG_EPTA, "epta update. bitmap:0x%x. fw:%d", + epta_freeze_bitmap, epta_freezed_wlan_duration_cfg); + + bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, hw_epta_enable_cfg); + + /* Exit freeze mode and recover the configuration of epta module; */ + if (epta_freeze_bitmap == 0) { + epta_freezed_wlan_duration_cfg = 0; + return -1; + } + + /* Enter freeze mode and set the configuration of the epta module; */ + /* select max wifi freeze value */ + for (i = 0; i < EPTA_FREEZE_MAX; i++) { + if (max_freeze < epta_freezed_wlan_duration[i]) + max_freeze = epta_freezed_wlan_duration[i]; + } + + /* same cfg don't change it */ + if (max_freeze == epta_freezed_wlan_duration_cfg) + return 0; + + epta_freezed_wlan_duration_cfg = max_freeze; + + /* only support epta_freezed_wlan_duration_cfg > wlan_duration_cfg */ + // wlan_duration_cfg may be override by wsm_epta_cmd + // if (max_freeze <= wlan_duration_cfg) { + // return 0; + // } + msg.wlan_duration = max_freeze; + msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze; + msg.hw_epta_enable = 0x00; + + return wsm_epta_cmd(hw_priv, &msg); +} + +/* + * coex_epta_freeze + * freeze coex duration for specific senario + * @wlan_duration: 20~100ms + * @type: + * EPTA_FREEZE_CONENCTING, + * EPTA_FREEZE_TTS, + * EPTA_FREEZE_THP, + * note: the value of wlan_duration should be less than 100000. + */ +int coex_epta_freeze(struct bes2600_common *hw_priv, int wlan_duration, uint32_t type) +{ + uint32_t i; + + epta_freeze_bitmap |= type; + + for (i = 0; i < EPTA_FREEZE_MAX; ++i) { + if (type & (1 << i)) + epta_freezed_wlan_duration[i] = wlan_duration; + } + /* reset epta value */ + epta_ps_mode = 0; + epta_adjust_cnt = 0; + + return coex_epta_freeze_update(hw_priv); +} + +static int coex_epta_recover_update(struct bes2600_common *hw_priv) +{ + struct wsm_epta_msg msg; + int i = 0; + int max_freeze = 0; + + if (hw_priv == NULL) { + bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n"); + return -1; + } + + bes2600_dbg(BES2600_DBG_EPTA, "epta update. bitmap:0x%x. fw:%d", + epta_freeze_bitmap, epta_freezed_wlan_duration_cfg); + + bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, hw_epta_enable_cfg); + + /* Exit freeze mode and recover the configuration of epta module; */ + if (epta_freeze_bitmap == 0) { + if (epta_freezed_wlan_duration_cfg == 0) + return -1; + epta_freezed_wlan_duration_cfg = 0; + // coex_set_epta_params will skip epta:0 after got_ip(use connected epta3) + msg.wlan_duration = wlan_duration_cfg; + msg.bt_duration = bt_duration_cfg; + msg.hw_epta_enable = hw_epta_enable_cfg; + return wsm_epta_cmd(hw_priv, &msg); + } + + /* Enter freeze mode and set the configuration of the epta module; */ + /* select max wifi freeze value */ + for (i = 0; i < EPTA_FREEZE_MAX; i++) { + if (max_freeze < epta_freezed_wlan_duration[i]) + max_freeze = epta_freezed_wlan_duration[i]; + } + + epta_freezed_wlan_duration_cfg = max_freeze; + msg.wlan_duration = max_freeze; + msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze; + msg.hw_epta_enable = hw_epta_enable_cfg; + + return wsm_epta_cmd(hw_priv, &msg); +} + +int coex_epta_recover(struct bes2600_common *hw_priv, uint32_t type) +{ + uint32_t i; + + if (epta_freeze_bitmap == 0) + return 0; + + if (type == EPTA_FREEZE_ALL) { + for (i = 0; i < EPTA_FREEZE_MAX; i++) { + epta_freezed_wlan_duration[i] = 0; + } + epta_freeze_bitmap = 0; + } else { + epta_freeze_bitmap &= (~type); + for (i = 0; i < EPTA_FREEZE_MAX; ++i) { + if (type & (1 << i)) + epta_freezed_wlan_duration[i] = 0; + } + } + return coex_epta_recover_update(hw_priv); +} + +int coex_epta_ps(struct bes2600_common *hw_priv, uint8_t enable) +{ + struct wsm_epta_msg msg; + if (hw_priv == NULL) { + bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n"); + return -1; + } + + if (enable && !epta_ps_mode) { + epta_ps_mode = 1; + msg.wlan_duration = EPTA_PS_WLAN_DURATION; + msg.bt_duration = EPTA_PS_BT_DURATION; + msg.hw_epta_enable = 0; + return wsm_epta_cmd(hw_priv, &msg); + } else if (!enable && epta_ps_mode) { + epta_ps_mode = 0; + msg.wlan_duration = wlan_duration_cfg; + msg.bt_duration = bt_duration_cfg; + msg.hw_epta_enable = 0; + return wsm_epta_cmd(hw_priv, &msg); + } + + return 0; +} + +int coex_epta_set_connect(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int epta) +{ + struct wsm_epta_msg msg; + if (!hw_priv) { + bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n"); + return -1; + } + if (epta != 4) { + msg.wlan_duration = (wlan_duration_cfg >= wlan_duration) ? wlan_duration_cfg : wlan_duration; + msg.bt_duration = (wlan_duration_cfg >= wlan_duration) ? bt_duration_cfg : bt_duration; + } else { + msg.wlan_duration = wlan_duration; + msg.bt_duration = bt_duration; + } + msg.hw_epta_enable = epta; + return wsm_epta_cmd(hw_priv, &msg); +} + +static void coex_set_wifi_state_to_fw(struct bes2600_common *hw_priv, uint8_t epta_conn_state) +{ + static uint8_t last_state; + + switch (epta_conn_state) { + case EPTA_STATE_WIFI_DISCONNECTED: + case EPTA_STATE_WIFI_CONNECTED: + if (last_state != epta_conn_state) { + last_state = epta_conn_state; + break; + } else { + return; + } + default: + return; + } + + bes2600_dbg(BES2600_DBG_EPTA, "%s state%u\r", __func__, last_state); + + switch (last_state) { + case EPTA_STATE_WIFI_DISCONNECTED: + wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_IDLE); + break; + case EPTA_STATE_WIFI_CONNECTED: + wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_CONNECTED); + break; + default: + break; + } +} + +/* TDD mode conn status change */ +void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status) +{ + if (connect_status != EPTA_STATE_WIFI_SCAN_COMP) { + if (epta_conn_state == connect_status) { + bes2600_dbg(BES2600_DBG_EPTA, "same connect_status:%d\r", connect_status); + return; + } + } + + if (connect_status == EPTA_STATE_WIFI_GOT_IP) { + if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED || epta_conn_state == EPTA_STATE_WIFI_CONNECTING) { + bes2600_dbg(BES2600_DBG_EPTA, "ignore got ip in disconnected\r"); + return; + } + } + epta_conn_state = connect_status; + + bes2600_dbg(BES2600_DBG_EPTA, "%s connect_status=%d coex_mode=%d\r", __func__, connect_status, g_coex_mode); + + coex_set_wifi_state_to_fw(hw_priv, epta_conn_state); + + if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT) { + if (g_coex_mode & WIFI_COEX_MODE_FDD_HYBRID_BIT) { + if (connect_status == EPTA_STATE_WIFI_CONNECTED) { + coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3); + } else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) { + coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests + /* HYBRID MODE: if wlan is disconnect, default use hw epta */ + coex_epta_set_connect(hw_priv, 100000, 0, 1); + } + } else { + if (connect_status == EPTA_STATE_WIFI_CONNECTED) { + // set bt bad channel according to wifi channel +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if (hw_priv->channel->band == NL80211_BAND_2GHZ) +#else + if (hw_priv->channel->band == IEEE80211_BAND_2GHZ) +#endif + wsm_epta_wifi_chan_cmd(hw_priv, hw_priv->channel->hw_value, hw_priv->ht_info.channel_type); + } else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) { + coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests + coex_epta_set_connect(hw_priv, 100000, 0, 1); + wsm_epta_wifi_chan_cmd(hw_priv, 0, 0); + } + } + } else { + int wlan_tdd_duration = EPTA_TDD_CONNECT_WIFI; + int bt_tdd_duration = EPTA_TDD_CONNECT_BT; + + /* if wlan_duration_cfg> EPTA_TDD_CONNECT_WIFI; chose wlan_duration_cfg, in TDD mode */ + if (wlan_duration_cfg > EPTA_TDD_CONNECT_WIFI) { + wlan_tdd_duration = wlan_duration_cfg; + bt_tdd_duration = bt_duration_cfg; + } + if (connect_status == EPTA_STATE_WIFI_CONNECTED) { + coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3); + } else if (connect_status == EPTA_STATE_WIFI_SCANNING) { + /* in scan status, only valid param 0, others wlan_tdd_duration,bt_tdd_duratio is invalid */ + coex_epta_freeze(hw_priv, wlan_duration_cfg, EPTA_FREEZE_SCANNING); + } else if (connect_status == EPTA_STATE_WIFI_SCAN_COMP) { + coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 0); + } else if (connect_status == EPTA_STATE_WIFI_GOT_IP) { + coex_epta_set_connect(hw_priv, 20000, 80000, 3); + coex_epta_recover(hw_priv, EPTA_FREEZE_SCANNING | EPTA_FREEZE_CONNECTING); + } else if (connect_status == EPTA_STATE_WIFI_CONNECTING) { + coex_epta_freeze(hw_priv, wlan_tdd_duration, EPTA_FREEZE_CONNECTING); + if (coex_fdd_mode == 0 && bt_duration_cfg > 50000) //bt audio + coex_epta_set_connect(hw_priv, 30000, 15000, 4); + } else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) { + coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests + /* TDD MODE: if wlan is disconnect, default use hw epta */ + // coex_epta_set_connect(hw_priv, 100000, 0, 1); + } else { + /* do nothing */ + } + } +} + +bool coex_is_wifi_inactive() +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s, epta_conn_state:%d", __FUNCTION__, epta_conn_state); + return epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED; +} +/* + * coex_set_epta_tts + * @tts_state: 0 - tts start, 1 - tts end + */ +void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state) +{ + if (EPTA_MODE_CFG == 0) { + if (tts_state) { + coex_epta_recover(hw_priv, EPTA_FREEZE_TTS); + } else { + coex_epta_freeze(hw_priv, 50000, EPTA_FREEZE_TTS); + } + } +} + +void coex_set_epta_thp(struct bes2600_common *hw_priv, uint32_t total_bps) +{ + int epta_adjust_inv = 3; + int wlan_tp_low; + + if (coex_fdd_mode && epta_conn_state >= EPTA_STATE_WIFI_GOT_IP) + return; + + //bes2600_dbg(BES2600_DBG_EPTA, "coex_set_epta_thp %d bt_state %d epta_freeze_bitmap=%d epta_adjust_cnt=%d coex_ps_en=%d fw=%d\n", + // total_bps, coex_bt_state, epta_freeze_bitmap, epta_adjust_cnt, coex_ps_en, epta_freezed_wlan_duration_cfg); + + /* sniffer mode always work with ble and tts, need adjust more quickly */ + // wlan_tp_low = netdev_sniffer_get_stat() ? EPTA_WLAN_TP_LOW : 500; + wlan_tp_low = 500; + + /* only need adjust in sw epta mode */ + if (EPTA_MODE_CFG != 0) + return; + + if ((coex_bt_state == 0) && (total_bps > EPTA_WLAN_TP_HIGH)) { + coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_HIGH, EPTA_FREEZE_THP); + } else if (total_bps > EPTA_WLAN_TP_MEDIUM) { + coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_MEDIUM, EPTA_FREEZE_THP); + } else if (total_bps > wlan_tp_low) { + coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_LOW, EPTA_FREEZE_THP); + } else { + if ((epta_freeze_bitmap & (1 << EPTA_FREEZE_THP)) && + (((++epta_adjust_cnt) % epta_adjust_inv) == 0)) { + // recover if wifi idle 3 secs + epta_adjust_cnt = 0; + coex_epta_recover(hw_priv, EPTA_FREEZE_THP); + } else if (coex_ps_en && !epta_freeze_bitmap) { + if ((total_bps > EPTA_WLAN_TP_PS)) { + coex_epta_ps(hw_priv, 0); + epta_adjust_cnt = 0; + } else if ((total_bps < EPTA_WLAN_TP_PS) && + (((++epta_adjust_cnt) % epta_adjust_inv) == 0)) { + coex_epta_ps(hw_priv, 1); + } + } + } +} + +// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band) +// { +// bes2600_info(BES2600_DBG_EPTA, "coex_band_update band:%u\n", (uint32_t)band); +// } + +void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected) +{ + bool fdd_en = 0; + + if (g_coex_mode == 0 || // tdd + g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) { + + bes2600_info(BES2600_DBG_EPTA, "coex_rssi_update rssi:%d, ch:%d, con:%d\n", + rssi, channel, connected); + if (channel > 14) { + fdd_en = 1; + ///TODO: HYBRID mode + // } else if (rssi >= COEX_FDD_RSSI_THR) { + // fdd_en = 1; + // } else if (rssi < COEX_TDD_RSSI_THR) { + // fdd_en = 0; + } + + if (fdd_en != coex_fdd_mode) { + coex_fdd_mode = fdd_en; + coex_set_epta_params(hw_priv, wlan_duration_cfg, bt_duration_cfg, hw_epta_enable_cfg); + } + } +} + +void coex_set_bt_state(struct bes2600_common *hw_priv, int state) +{ + bes2600_info(BES2600_DBG_EPTA, "coex_set_bt_state %d\n", state); + coex_bt_state = state; +} + +void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp) +{ + if (connected) { + /* + * Adjust wifi/bt duration dynamically according to throughput for a better performance; + */ + coex_set_epta_thp(hw_priv, tp); + } + + if (g_coex_mode == 0 || // tdd + g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) { + coex_rssi_update(hw_priv, rssi, channel, connected); + } +} + +/* + * set fdd or fdd hybrid + * 1: fdd + * 0: fddhybrid + */ +void coex_set_fdd_mode(bool fdd_mode) +{ + if (g_coex_mode == 0 || // tdd + g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) { //hybrid + bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, fdd_mode); + coex_fdd_mode = fdd_mode; + } +} + +bool coex_is_fdd_mode() +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, coex_fdd_mode); + return coex_fdd_mode; +} + +bool coex_is_bt_a2dp() +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state); + return coex_bt_state == 3; +} + +bool coex_is_bt_inactive() +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state); + return coex_bt_state == 0; +} + +int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode) +{ + bes2600_info(BES2600_DBG_EPTA, "coex_init_mode coex_mode %d\n", coex_mode); + + g_coex_mode = coex_mode; + + if (coex_mode & WIFI_COEX_MODE_FDD_BIT) + coex_fdd_mode = true; + else + coex_fdd_mode = false; + + coex_wifi_bt_ts_thread_init(hw_priv); + return 0; +} + +int coex_deinit_mode(struct bes2600_common *hw_priv) +{ + bes2600_info(BES2600_DBG_EPTA, "coex_deinit_mode\n"); + + coex_wifi_bt_ts_thread_deinit(hw_priv); + + return 0; +} + +int coex_start(struct bes2600_common *hw_priv) +{ + bes2600_info(BES2600_DBG_EPTA, "%s\n", __FUNCTION__); + + coex_ps_en = false; + if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT) + coex_fdd_mode = true; + else + coex_fdd_mode = false; + epta_conn_state = 0xff; + epta_freeze_bitmap = 0; + epta_freezed_wlan_duration_cfg = 0; + wlan_duration_cfg = 20000; + bt_duration_cfg = 80000; + hw_epta_enable_cfg = 0; + epta_ps_mode = 0; + epta_adjust_cnt = 0; + coex_bt_state = 0; + + return 0; +} + +int coex_stop(struct bes2600_common *hw_priv) +{ + bes2600_info(BES2600_DBG_EPTA, "%s\n", __FUNCTION__); + return 0; +} diff --git a/drivers/staging/bes2600/epta_coex.h b/drivers/staging/bes2600/epta_coex.h new file mode 100644 index 000000000000..bc9eed6cc794 --- /dev/null +++ b/drivers/staging/bes2600/epta_coex.h @@ -0,0 +1,86 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __EPTA_COEX_H__ +#define __EPTA_COEX_H__ + +#include +#include + +#include "bes2600.h" + +#define WIFI_COEX_MODE_FDD_BIT (1<<0) +#define WIFI_COEX_MODE_FDD_HYBRID_BIT (1<<1) + +enum bwifi_epta_state { + EPTA_STATE_WIFI_DISCONNECTED = 0, + EPTA_STATE_WIFI_SCANNING = 1, + EPTA_STATE_WIFI_SCAN_COMP = 2, + EPTA_STATE_WIFI_CONNECTING = 3, + EPTA_STATE_WIFI_CONNECTED = 4, + EPTA_STATE_WIFI_GOT_IP = 5, + EPTA_STATE_WIFI_TTS_START = 6, + EPTA_STATE_WIFI_TTS_END = 7, + + EPTA_STATE_NUM +}; + +/* max FREEZE BIT */ +#define EPTA_FREEZE_MAX 32 +typedef enum { + EPTA_FREEZE_SCANNING = 1 << 0, + EPTA_FREEZE_CONNECTING = 1 << 1, + EPTA_FREEZE_TTS = 1 << 2, + EPTA_FREEZE_THP = 1 << 3, + /* clear all FREEZE BIT */ + EPTA_FREEZE_ALL +} EPTA_FREEZE_TYPE_T; + +#ifndef COEX_TDD_RSSI_THR +#define COEX_TDD_RSSI_THR (-15) +#endif +#ifndef COEX_FDD_RSSI_THR +#define COEX_FDD_RSSI_THR (-10) +#endif + +#define EPTA_PS_WLAN_DURATION (20000) +#define EPTA_PS_BT_DURATION (80000) +#define EPTA_ADJUST_WLAN_DURATION_HIGH (80000) +#define EPTA_ADJUST_WLAN_DURATION_MEDIUM (55000) +#define EPTA_ADJUST_WLAN_DURATION_LOW (40000) +#define EPTA_WLAN_TP_HIGH (4000) +#define EPTA_WLAN_TP_MEDIUM (2000) +#define EPTA_WLAN_TP_LOW (100) +#define EPTA_WLAN_TP_PS (50) +#define EPTA_FREEZE_TDD_PERIOD (102400) +#define EPTA_TDD_CONNECT_WIFI (50000) +#define EPTA_TDD_CONNECT_BT (50000) + +int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duraiton, int bt_duration, int hw_epta_enable); +void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp); +void coex_set_bt_state(struct bes2600_common *hw_priv, int state); +void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status); +void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state); +int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode); +int coex_deinit_mode(struct bes2600_common *hw_priv); +int coex_start(struct bes2600_common *hw_priv); +int coex_stop(struct bes2600_common *hw_priv); + +void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected); +// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band); +bool coex_is_fdd_mode(void); +void coex_set_fdd_mode(bool fdd_mode); +bool coex_is_bt_a2dp(void); +bool coex_is_bt_inactive(void); +bool coex_is_wifi_inactive(void); + +#endif + + diff --git a/drivers/staging/bes2600/epta_request.c b/drivers/staging/bes2600/epta_request.c new file mode 100644 index 000000000000..44676dcbc6d7 --- /dev/null +++ b/drivers/staging/bes2600/epta_request.c @@ -0,0 +1,650 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#include "epta_coex.h" +#include "epta_request.h" + +#define EPTA_PERIOD_TIME 102400 + +#define BT_TIME_MAX 80000 + +#define BT_SHUTDOWN_TIME 0 +#define BT_SHUTDOWN_MIN_TIME 0 +#define BT_DISCONNECTED_TIME 0 +#define BT_DISCONNECTED_MIN_TIME 0 +#define BT_CONNECTING_TIME 60000 +#define BT_CONNECTING_MIN_TIME 0 +#define BT_CONNECTED_TIME 0 +#define BT_CONNECTED_MIN_TIME 25000 +#define BT_CONNECTED_SNIFF_TIME 0 +#define BT_CONNECTED_SNIFF_MIN_TIME 20000 + +#define BT_BOTHSCAN_DISABLE_TIME 0 +#define BT_BOTHSCAN_DISABLE_MIN_TIME 0 +#define BT_BOTHSCAN_ENABLE_TIME 0 +#define BT_BOTHSCAN_ENABLE_MIN_TIME 20000 +#define BT_PSCAN_ENABLE_TIME 0 +#define BT_PSCAN_ENABLE_MIN_TIME 20000 +#define BT_ISCAN_ENABLE_TIME 0 +#define BT_ISCAN_ENABLE_MIN_TIME 20000 + +#define BT_AUDIO_NONE_TIME 0 +#define BT_AUDIO_NONE_MIN_TIME 0 +#define BT_AUDIO_A2DP_TIME 60000 +#define BT_AUDIO_A2DP_MIN_TIME 0 +#define BT_AUDIO_SCO_TIME 80000 +#define BT_AUDIO_SCO_MIN_TIME 0 + +#define BT_INQ_START_TIME 20000 +#define BT_INQ_START_MIN_TIME 30000 +#define BT_INQ_STOP_TIME 0 +#define BT_INQ_STOP_MIN_TIME 0 + +#define BT_LE_SCAN_START_TIME 20000 +#define BT_LE_SCAN_START_MIN_TIME 30000 +#define BT_LE_SCAN_STOP_TIME 0 +#define BT_LE_SCAN_STOP_MIN_TIME 0 + +#define BT_LE_ADV_START_TIME 20000 +#define BT_LE_ADV_START_MIN_TIME 40000 +#define BT_LE_ADV_STOP_TIME 0 +#define BT_LE_ADV_STOP_MIN_TIME 0 + +#define BT_LE_CONNECTED_TIME 0 +#define BT_LE_CONNECTED_MIN_TIME 50000 +#define BT_LE_DISCONNECTED_TIME 0 +#define BT_LE_DISCONNECTED_MIN_TIME 0 + +// BIT[2:0] +#define BT_REQUEST_STATUS_SHIFT 0 +#define BT_REQUEST_STATUS_MASK (0x7 << BT_REQUEST_STATUS_SHIFT) +#define BT_REQUEST_STATUS_VALUE(n) (((n) & BT_REQUEST_STATUS_MASK) >> BT_REQUEST_STATUS_SHIFT) + +// BIT[5:3] +#define BT_REQUEST_SCAN_SHIFT 3 +#define BT_REQUEST_SCAN_MASK (0x7 << BT_REQUEST_SCAN_SHIFT) +#define BT_REQUEST_SCAN_VALUE(n) (((n) & BT_REQUEST_SCAN_MASK) >> BT_REQUEST_SCAN_SHIFT) + +// BIT[8:6] +#define BT_REQUEST_AUDIO_SHIFT 6 +#define BT_REQUEST_AUDIO_MASK (0x7 << BT_REQUEST_AUDIO_SHIFT) +#define BT_REQUEST_AUDIO_VALUE(n) (((n) & BT_REQUEST_AUDIO_MASK) >> BT_REQUEST_AUDIO_SHIFT) + +// BIT[10:9] +#define BT_REQUEST_INQ_SHIFT 9 +#define BT_REQUEST_INQ_MASK (0x3 << BT_REQUEST_INQ_SHIFT) +#define BT_REQUEST_INQ_VALUE(n) (((n) & BT_REQUEST_INQ_MASK) >> BT_REQUEST_INQ_SHIFT) + +// BIT[12:11] +#define BT_REQUEST_LE_SCAN_SHIFT 11 +#define BT_REQUEST_LE_SCAN_MASK (0x3 << BT_REQUEST_LE_SCAN_SHIFT) +#define BT_REQUEST_LE_SCAN_VALUE(n) (((n) & BT_REQUEST_LE_SCAN_MASK) >> BT_REQUEST_LE_SCAN_SHIFT) + +// BIT[15:13] +#define BT_REQUEST_LE_ADV_SHIFT 13 +#define BT_REQUEST_LE_ADV_MASK (0x7 << BT_REQUEST_LE_ADV_SHIFT) +#define BT_REQUEST_LE_ADV_VALUE(n) (((n) & BT_REQUEST_LE_ADV_MASK) >> BT_REQUEST_LE_ADV_SHIFT) + +// BIT[17:16] +#define BT_REQUEST_LE_STATUS_SHIFT 16 +#define BT_REQUEST_LE_STATUS_MASK (0x3 << BT_REQUEST_LE_STATUS_SHIFT) +#define BT_REQUEST_LE_STATUS_VALUE(n) (((n) & BT_REQUEST_LE_STATUS_MASK) >> BT_REQUEST_LE_STATUS_SHIFT) + +typedef enum { + BWIFI_BT_STATUS_SHUTDOWN = 0, + BWIFI_BT_STATUS_DISCONNECTED = 1, + BWIFI_BT_STATUS_CONNECTING = 2, + BWIFI_BT_STATUS_CONNECTED_SNIFF = 3,// + BWIFI_BT_STATUS_CONNECTED = 4,// +} BWIFI_BT_STATUS_T; + +typedef enum { + BWIFI_BT_BOTHSCAN_DISABLE = 0, + BWIFI_BT_BOTHSCAN_ENABLE = 1, + BWIFI_BT_PSCAN_ENABLE = 2, + BWIFI_BT_ISCAN_ENABLE = 3, +} BWIFI_BT_SCAN_T; + +typedef enum { + BWIFI_BT_AUDIO_NONE = 0, + BWIFI_BT_AUDIO_A2DP = 1, + BWIFI_BT_AUDIO_SCO = 2, +} BWIFI_BT_AUDIO_T; + +typedef enum { + BWIFI_BT_INQ_STOP = 0,// + BWIFI_BT_INQ_START = 1,// +} BWIFI_BT_INQ_T; + +typedef enum { + BWIFI_LE_SCAN_STOP = 0,// + BWIFI_LE_SCAN_START = 1,// +} BWIFI_BT_LE_SCAN_T; + +typedef enum { + BWIFI_LE_ADV_STOP = 0,// + BWIFI_LE_ADV_START = 1,// +} BWIFI_BT_LE_ADV_T; + +typedef enum { + BWIFI_LE_DISCONNECTED = 0,// + BWIFI_LE_CONNECTED = 1,// +} BWIFI_BT_LE_STATUS_T; + +enum COEX_BT_OPER_T { + COEX_BT_OPER_STATUS, + COEX_BT_OPER_SCAN, + COEX_BT_OPER_AUDIO, + COEX_BT_OPER_INQ, + COEX_BT_OPER_LE_SCAN, + COEX_BT_OPER_LE_ADV, + COEX_BT_OPER_LE_STATUS, + + COEX_BT_OPER_NUM, +}; + +union COEX_BT_OPER_TYPE_T { + BWIFI_BT_STATUS_T status; + BWIFI_BT_SCAN_T scan; + BWIFI_BT_AUDIO_T audio; + BWIFI_BT_INQ_T inq; + BWIFI_BT_LE_SCAN_T le_scan; + BWIFI_BT_LE_ADV_T le_adv; + BWIFI_BT_LE_STATUS_T le_status; +}; + +struct COEX_BT_OPER_TIME_T { + enum COEX_BT_OPER_T oper; + union COEX_BT_OPER_TYPE_T type; + uint32_t time; + uint32_t min_time; +}; + +struct COEX_BT_OPER_TIME_T g_coex_bt_oper[COEX_BT_OPER_NUM]; + +static void coex_bt_time_init(void) +{ + memset(g_coex_bt_oper, 0, sizeof(g_coex_bt_oper)); + g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_SHUTDOWN; + g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_DISABLE; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE; + g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_STOP; + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_STOP; + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_STOP; + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_DISCONNECTED; +} + +static uint32_t coex_calc_bt_time(void) +{ + uint32_t i; + uint32_t time = 0, min_time = 0; + + for (i = 0; i < COEX_BT_OPER_NUM; ++i) { + time += g_coex_bt_oper[i].time; + if (min_time < g_coex_bt_oper[i].min_time) + min_time = g_coex_bt_oper[i].min_time; + } + + bes2600_dbg(BES2600_DBG_EPTA, "%s time:%u, min_time:%u", __func__, time, min_time); + time = time < min_time ? min_time : time; + return time < BT_TIME_MAX ? time : BT_TIME_MAX; +} + +void coex_calc_wifi_scan_time(uint32_t *min_chan, uint32_t *max_chan) +{ + uint32_t time = coex_calc_bt_time(); + + if (time == 0) { + *min_chan = 90; + *max_chan = 90; + } else if (time < 40000) { + *min_chan = 50; + *max_chan = 110; + } else if (time < 60000) { + *min_chan = 40; + *max_chan = 110; + } else if (time < 80000) { + *min_chan = 30; + *max_chan = 120; + } else { + *min_chan = 30; + *max_chan = 130; + } +} + +static void coex_bt_state_notify(struct bes2600_common *hw_priv) +{ + int32_t wifi_dur, bt_dur, mode; + + bt_dur = coex_calc_bt_time(); + wifi_dur = EPTA_PERIOD_TIME - bt_dur; + mode = 0; + coex_set_epta_params(hw_priv, wifi_dur, bt_dur, mode); +} + +static void coex_bt_oper_status(struct bes2600_common *hw_priv, BWIFI_BT_STATUS_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_BT_STATUS_SHUTDOWN: + coex_bt_time_init(); + break; + case BWIFI_BT_STATUS_DISCONNECTED: + g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_DISCONNECTED; + g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_DISCONNECTED_TIME; + g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_DISCONNECTED_MIN_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME; + break; + case BWIFI_BT_STATUS_CONNECTING: + g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTING; + g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTING_TIME; + g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTING_MIN_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME; + break; + case BWIFI_BT_STATUS_CONNECTED: + g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTED; + g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTED_TIME; + g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTED_MIN_TIME; + break; + case BWIFI_BT_STATUS_CONNECTED_SNIFF: + g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTED_SNIFF; + g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTED_SNIFF_TIME; + g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTED_SNIFF_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static void coex_bt_oper_scan(struct bes2600_common *hw_priv, BWIFI_BT_SCAN_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_BT_BOTHSCAN_DISABLE: + g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_DISABLE; + g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_BOTHSCAN_DISABLE_TIME; + g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_BOTHSCAN_DISABLE_MIN_TIME; + break; + case BWIFI_BT_BOTHSCAN_ENABLE: + g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_ENABLE; + g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_BOTHSCAN_ENABLE_TIME; + g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_BOTHSCAN_ENABLE_MIN_TIME; + break; + case BWIFI_BT_PSCAN_ENABLE: + g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_PSCAN_ENABLE; + g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_PSCAN_ENABLE_TIME; + g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_PSCAN_ENABLE_MIN_TIME; + break; + case BWIFI_BT_ISCAN_ENABLE: + g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_ISCAN_ENABLE; + g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_ISCAN_ENABLE_TIME; + g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_ISCAN_ENABLE_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static void coex_bt_oper_audio(struct bes2600_common *hw_priv, BWIFI_BT_AUDIO_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_BT_AUDIO_NONE: + g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME; + break; + case BWIFI_BT_AUDIO_A2DP: + g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_A2DP; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_A2DP_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_A2DP_MIN_TIME; + break; + case BWIFI_BT_AUDIO_SCO: + g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_SCO; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_SCO_TIME; + g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_SCO_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static void coex_bt_oper_inq(struct bes2600_common *hw_priv, BWIFI_BT_INQ_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_BT_INQ_START: + g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_START; + g_coex_bt_oper[COEX_BT_OPER_INQ].time = BT_INQ_START_TIME; + g_coex_bt_oper[COEX_BT_OPER_INQ].min_time = BT_INQ_START_MIN_TIME; + break; + case BWIFI_BT_INQ_STOP: + g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_STOP; + g_coex_bt_oper[COEX_BT_OPER_INQ].time = BT_INQ_STOP_TIME; + g_coex_bt_oper[COEX_BT_OPER_INQ].min_time = BT_INQ_STOP_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static void coex_bt_oper_le_scan(struct bes2600_common *hw_priv, BWIFI_BT_LE_SCAN_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_LE_SCAN_START: + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_START; + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].time = BT_LE_SCAN_START_TIME; + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].min_time = BT_LE_SCAN_START_MIN_TIME; + break; + case BWIFI_LE_SCAN_STOP: + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_STOP; + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].time = BT_LE_SCAN_STOP_TIME; + g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].min_time = BT_LE_SCAN_STOP_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static void coex_bt_oper_le_adv(struct bes2600_common *hw_priv, BWIFI_BT_LE_ADV_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_LE_ADV_START: + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_START; + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].time = BT_LE_ADV_START_TIME; + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].min_time = BT_LE_ADV_START_MIN_TIME; + break; + case BWIFI_LE_ADV_STOP: + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_STOP; + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].time = BT_LE_ADV_STOP_TIME; + g_coex_bt_oper[COEX_BT_OPER_LE_ADV].min_time = BT_LE_ADV_STOP_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static void coex_bt_oper_le_status(struct bes2600_common *hw_priv, BWIFI_BT_LE_STATUS_T type) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type); + + switch (type) { + case BWIFI_LE_CONNECTED: + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_CONNECTED; + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].time = BT_LE_CONNECTED_TIME; + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].min_time = BT_LE_CONNECTED_MIN_TIME; + break; + case BWIFI_LE_DISCONNECTED: + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_DISCONNECTED; + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].time = BT_LE_DISCONNECTED_TIME; + g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].min_time = BT_LE_DISCONNECTED_MIN_TIME; + break; + default: + bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type); + break; + } +} + +static int coex_wifi_state_notify(struct bes2600_common *hw_priv, enum bwifi_epta_state state) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s state:%d", __func__, state); + switch (state) { + case EPTA_STATE_WIFI_DISCONNECTED: + coex_set_wifi_conn(hw_priv, state); + break; + case EPTA_STATE_WIFI_SCANNING: + coex_set_wifi_conn(hw_priv, state); + break; + case EPTA_STATE_WIFI_SCAN_COMP: + coex_set_wifi_conn(hw_priv, state); + break; + case EPTA_STATE_WIFI_CONNECTING: + coex_set_wifi_conn(hw_priv, state); + break; + case EPTA_STATE_WIFI_CONNECTED: + coex_set_wifi_conn(hw_priv, state); + break; + case EPTA_STATE_WIFI_GOT_IP: + coex_set_wifi_conn(hw_priv, state); + break; + case EPTA_STATE_WIFI_TTS_START: + coex_set_epta_tts(hw_priv, 0); + break; + case EPTA_STATE_WIFI_TTS_END: + coex_set_epta_tts(hw_priv, 1); + break; + default: + return -1; + } + return 0; +} + +static void coex_wifi_idle(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(false); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_DISCONNECTED); +} + +static void coex_wifi_scanning(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(false); //scan use tdd + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_SCANNING); +} + +static void coex_wifi_scan_comp(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_SCAN_COMP); +} + +static void coex_wifi_connecting(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(false); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTING); +} + +static void coex_wifi_connecting_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(true); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTING); +} + +static void coex_wifi_connected(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTED); +} + +static void coex_wifi_connected_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(true); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTED); +} + +static void coex_wifi_got_ip(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_GOT_IP); +} + +static void coex_wifi_got_ip_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(true); // used for scan -> got ip 5g + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_GOT_IP); +} + +static void coex_wifi_disconnecting(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); +} + +static void coex_wifi_disconnected(struct bes2600_common *hw_priv, BWIFI_STATUS_T value) +{ + bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__); + coex_set_fdd_mode(false); + coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_DISCONNECTED); +} + +static int coex_wifi_bt_ts_request(struct bes2600_common *hw_priv, COEX_TS_TYPE_T type, uint32_t value) +{ + COEX_WIFI_BT_TS_T *wifi_bt_ts_event; + + bes2600_info(BES2600_DBG_EPTA, "%s type:%u, value:0x%x", __func__, type, value); + + if (atomic_read(&hw_priv->netdevice_start) == 0) { + bes2600_info(BES2600_DBG_EPTA, "net down. skip"); + return 0; + } + + /* called from spin lock vif_lock context */ + wifi_bt_ts_event = kmalloc(sizeof(COEX_WIFI_BT_TS_T), GFP_ATOMIC); + if (wifi_bt_ts_event == NULL) { + bes2600_err(BES2600_DBG_EPTA, "ts_event: malloc fail"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&wifi_bt_ts_event->node); + wifi_bt_ts_event->type = type; + wifi_bt_ts_event->value = value; + + spin_lock(&hw_priv->coex_event_lock); + list_add_tail(&wifi_bt_ts_event->node, &hw_priv->coex_event_list); + spin_unlock(&hw_priv->coex_event_lock); + + schedule_work(&hw_priv->coex_work); + + return 0; +} + +void bbt_change_current_status(struct bes2600_common *hw_priv, uint32_t new_status) +{ + coex_wifi_bt_ts_request(hw_priv, COEX_TS_TYPE_BT, new_status); +} + +void bwifi_change_current_status(struct bes2600_common *hw_priv, BWIFI_STATUS_T new_status) +{ + coex_wifi_bt_ts_request(hw_priv, COEX_TS_TYPE_WIFI, new_status); +} + +static void coex_wifi_bt_ts_cb(struct bes2600_common *hw_priv, COEX_WIFI_BT_TS_T *evt) +{ + if (evt->type == COEX_TS_TYPE_BT) { + coex_bt_oper_scan(hw_priv, (BWIFI_BT_SCAN_T)BT_REQUEST_SCAN_VALUE(evt->value)); + coex_bt_oper_audio(hw_priv, (BWIFI_BT_AUDIO_T)BT_REQUEST_AUDIO_VALUE(evt->value)); + coex_bt_oper_inq(hw_priv, (BWIFI_BT_INQ_T)BT_REQUEST_INQ_VALUE(evt->value)); + coex_bt_oper_le_scan(hw_priv, (BWIFI_BT_LE_SCAN_T)BT_REQUEST_LE_SCAN_VALUE(evt->value)); + coex_bt_oper_le_adv(hw_priv, (BWIFI_BT_LE_ADV_T)BT_REQUEST_LE_ADV_VALUE(evt->value)); + coex_bt_oper_le_status(hw_priv, (BWIFI_BT_LE_STATUS_T)BT_REQUEST_LE_STATUS_VALUE(evt->value)); + // process BT STATUS in the end + coex_bt_oper_status(hw_priv, (BWIFI_BT_STATUS_T)BT_REQUEST_STATUS_VALUE(evt->value)); + coex_bt_state_notify(hw_priv); + } else if (evt->type == COEX_TS_TYPE_WIFI) { + switch (evt->value) { + case BWIFI_STATUS_IDLE: + coex_wifi_idle(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_SCANNING: + coex_wifi_scanning(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_SCANNING_COMP: + coex_wifi_scan_comp(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_CONNECTING: + coex_wifi_connecting(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_CONNECTING_5G: + coex_wifi_connecting_5g(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_CONNECTED: + coex_wifi_connected(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_CONNECTED_5G: + coex_wifi_connected_5g(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_GOT_IP: + coex_wifi_got_ip(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_GOT_IP_5G: + coex_wifi_got_ip_5g(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_DISCONNECTING: + coex_wifi_disconnecting(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + case BWIFI_STATUS_DISCONNECTED: + coex_wifi_disconnected(hw_priv, (BWIFI_STATUS_T)evt->value); + break; + default: + bes2600_err(BES2600_DBG_EPTA, "UNKNOWN WIFI type %d", evt->value); + break; + } + } else { + bes2600_err(BES2600_DBG_EPTA, "UNKNOWN EPTA type %d, %d", evt->type, evt->value); + } +} + +static void coex_wifi_bt_ts_thread(struct work_struct *work) +{ + COEX_WIFI_BT_TS_T *coex_event; + struct bes2600_common *hw_priv = container_of(work, struct bes2600_common, coex_work); + + spin_lock(&hw_priv->coex_event_lock); + while (!list_empty(&hw_priv->coex_event_list)) { + coex_event = list_first_entry(&hw_priv->coex_event_list, COEX_WIFI_BT_TS_T, node); + spin_unlock(&hw_priv->coex_event_lock); + coex_wifi_bt_ts_cb(hw_priv, coex_event); + list_del(&coex_event->node); + kfree(coex_event); + + spin_lock(&hw_priv->coex_event_lock); + } + spin_unlock(&hw_priv->coex_event_lock); +} + +void coex_wifi_bt_ts_thread_init(struct bes2600_common *hw_priv) +{ + coex_bt_time_init(); + + INIT_WORK(&hw_priv->coex_work, coex_wifi_bt_ts_thread); + INIT_LIST_HEAD(&hw_priv->coex_event_list); + spin_lock_init(&hw_priv->coex_event_lock); +} + +void coex_wifi_bt_ts_thread_deinit(struct bes2600_common *hw_priv) +{ + cancel_work_sync(&hw_priv->coex_work); +} diff --git a/drivers/staging/bes2600/epta_request.h b/drivers/staging/bes2600/epta_request.h new file mode 100644 index 000000000000..f0217c2c859d --- /dev/null +++ b/drivers/staging/bes2600/epta_request.h @@ -0,0 +1,52 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef EPTA_REQUEST_H +#define EPTA_REQUEST_H + +#include "linux/list.h" +#include "bes2600.h" + +typedef enum { + BWIFI_STATUS_IDLE = 0, + BWIFI_STATUS_DISCONNECTING = 1, + BWIFI_STATUS_SCANNING = 2, + BWIFI_STATUS_CONNECTING = 3, + BWIFI_STATUS_WPS_CONNECTING = 4, + BWIFI_STATUS_CONNECTED = 5, + BWIFI_STATUS_DHCPING = 6, + BWIFI_STATUS_GOT_IP = 7, + /* Warning: don't change enum value above, it's aligned with fw */ + + BWIFI_STATUS_CONNECTING_5G = 8, + BWIFI_STATUS_CONNECTED_5G = 9, + BWIFI_STATUS_DISCONNECTED = 10, + BWIFI_STATUS_GOT_IP_5G = 11, + BWIFI_STATUS_SCANNING_5G = 12, + BWIFI_STATUS_SCANNING_COMP = 13, +} BWIFI_STATUS_T; + +typedef enum { + COEX_TS_TYPE_BT, + COEX_TS_TYPE_WIFI, +} COEX_TS_TYPE_T; + +typedef struct { + struct list_head node; + COEX_TS_TYPE_T type; + uint32_t value; +} COEX_WIFI_BT_TS_T; + +void bbt_change_current_status(struct bes2600_common *hw_priv, uint32_t new_status); +void bwifi_change_current_status(struct bes2600_common *hw_priv, BWIFI_STATUS_T new_status); +void coex_wifi_bt_ts_thread_init(struct bes2600_common *hw_priv); +void coex_wifi_bt_ts_thread_deinit(struct bes2600_common *hw_priv); +void coex_calc_wifi_scan_time(uint32_t *min_chan, uint32_t *max_chan); +#endif /*EPTA_REQUEST_H*/ \ No newline at end of file diff --git a/drivers/staging/bes2600/fwio.c b/drivers/staging/bes2600/fwio.c new file mode 100644 index 000000000000..05763fe73b06 --- /dev/null +++ b/drivers/staging/bes2600/fwio.c @@ -0,0 +1,55 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "bes2600.h" +#include "fwio.h" +#include "hwio.h" +#include "sbus.h" +#include "bh.h" + +#ifdef FW_DOWNLOAD_BY_SDIO +extern int bes2600_load_firmware_sdio(struct sbus_ops *ops, struct sbus_priv *priv); +#endif + +#ifdef FW_DOWNLOAD_BY_USB +extern int bes2600_load_firmware_usb(struct sbus_ops *ops, struct sbus_priv *priv); +#endif + +#ifdef FW_DOWNLOAD_BY_UART +extern int bes2600_load_firmware_uart(struct sbus_ops *ops, struct sbus_priv *priv); +#endif + +int bes2600_load_firmware(struct sbus_ops *ops, struct sbus_priv *priv) +{ + int ret = 0; + +#if defined(FW_DOWNLOAD_BY_SDIO) + if ((ret = bes2600_load_firmware_sdio(ops, priv))) + return ret; +#endif + +#if defined(FW_DOWNLOAD_BY_USB) + if ((ret = bes2600_load_firmware_usb(ops, priv))) + return ret; +#endif + +#if defined(FW_DOWNLOAD_BY_UART) + if ((ret = bes2600_load_firmware_uart(ops, priv))) + return ret; +#endif + + return ret; +} diff --git a/drivers/staging/bes2600/fwio.h b/drivers/staging/bes2600/fwio.h new file mode 100644 index 000000000000..a4afb7ab17e2 --- /dev/null +++ b/drivers/staging/bes2600/fwio.h @@ -0,0 +1,48 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define FIRMWARE_1250_CUT11 ("wsm_5011.bin") +#define FIRMWARE_CUT22 ("wsm_22.bin") +#define FIRMWARE_CUT20 ("wsm_20.bin") +#define FIRMWARE_CUT11 ("wsm_11.bin") +#define FIRMWARE_CUT10 ("wsm_10.bin") +#if defined(BES2600_DETECTION_LOGIC) +#define FIRMWARE_1260_CUT10 ("wsm_6010.bin") +#endif +#define SDD_FILE_1250_11 ("sdd_5011.bin") +#define SDD_FILE_22 ("sdd_22.bin") +#define SDD_FILE_20 ("sdd_20.bin") +#define SDD_FILE_11 ("sdd_11.bin") +#define SDD_FILE_10 ("sdd_10.bin") +#if defined(BES2600_DETECTION_LOGIC) +#define SDD_FILE_1260_10 ("sdd_6010.bin") +#endif +#if defined(BES2600_DETECTION_LOGIC) +#define BOOTLOADER_FILE_1260 ("bootloader_1260.bin") +#endif + +#define BES2600_HW_REV_CUT10 (10) +#define BES2600_HW_REV_CUT11 (11) +#define BES2600_HW_REV_CUT20 (20) +#define BES2600_HW_REV_CUT22 (22) +#define CW1250_HW_REV_CUT10 (110) +#define CW1250_HW_REV_CUT11 (5011) +#if defined(BES2600_DETECTION_LOGIC) +#define BES2600_HW_REV_CUT10 (6010) +#endif +struct sbus_ops; +struct sbus_priv; + +int bes2600_load_firmware(struct sbus_ops *ops, struct sbus_priv *priv); + +#endif diff --git a/drivers/staging/bes2600/ht.h b/drivers/staging/bes2600/ht.h new file mode 100644 index 000000000000..b5caa291936a --- /dev/null +++ b/drivers/staging/bes2600/ht.h @@ -0,0 +1,43 @@ +/* + * HT-related code for BES2600 driver + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_HT_H_INCLUDED +#define BES2600_HT_H_INCLUDED + +#include + +struct bes2600_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +static inline int bes2600_is_ht(const struct bes2600_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int bes2600_ht_greenfield(const struct bes2600_ht_info *ht_info) +{ + return bes2600_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static inline int bes2600_ht_ampdu_density(const struct bes2600_ht_info *ht_info) +{ + if (!bes2600_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* BES2600_HT_H_INCLUDED */ diff --git a/drivers/staging/bes2600/hwio.c b/drivers/staging/bes2600/hwio.c new file mode 100644 index 000000000000..17ed27adc555 --- /dev/null +++ b/drivers/staging/bes2600/hwio.c @@ -0,0 +1,368 @@ +/* + * Low-level device IO routines for BES2600 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "bes2600.h" +#include "hwio.h" +#include "sbus.h" + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) +#define MAX_RETRY 3 + +static struct sbus_ops *bes2600_subs_ops = NULL; +static struct sbus_priv *bes2600_sbus_priv = NULL; + +static int __bes2600_reg_read(u16 addr, void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + bes2600_err(BES2600_DBG_SBUS, + "%s: buffer is not aligned.\n", __func__); + return -EINVAL; + } + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + BUG_ON(!bes2600_subs_ops); + return bes2600_subs_ops->sbus_memcpy_fromio(bes2600_sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __bes2600_reg_write(u16 addr, const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + +#if 0 + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + bes2600_dbg(BES2600_DBG_SBUS, "%s: buffer is not aligned.\n", + __func__); + return -EINVAL; + } +#endif + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + BUG_ON(!bes2600_subs_ops); + return bes2600_subs_ops->sbus_memcpy_toio(bes2600_sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __bes2600_reg_read_32(u16 addr, u32 *val) +{ + return __bes2600_reg_read(addr, val, sizeof(val), 0); +} + +static inline int __bes2600_reg_write_32(u16 addr, u32 val) +{ + return __bes2600_reg_write(addr, &val, sizeof(val), 0); +} + +void bes2600_reg_set_object(struct sbus_ops *ops, struct sbus_priv *priv) +{ + bes2600_subs_ops = ops; + bes2600_sbus_priv = priv; +} + +int bes2600_reg_read(u32 addr, void *buf, size_t buf_len) +{ + int ret; + BUG_ON(!bes2600_subs_ops); + bes2600_subs_ops->lock(bes2600_sbus_priv); + ret = bes2600_subs_ops->sbus_reg_read(bes2600_sbus_priv, addr, buf, buf_len); + bes2600_subs_ops->unlock(bes2600_sbus_priv); + return ret; +} + +int bes2600_reg_write(u32 addr, const void *buf, size_t buf_len) +{ + int ret; + BUG_ON(!bes2600_subs_ops); + bes2600_subs_ops->lock(bes2600_sbus_priv); + ret = bes2600_subs_ops->sbus_reg_write(bes2600_sbus_priv, addr, buf, buf_len); + bes2600_subs_ops->unlock(bes2600_sbus_priv); + return ret; +} + +int bes2600_data_read(void *buf, size_t buf_len) +{ + int ret, retry = 1; + BUG_ON(!bes2600_subs_ops); + bes2600_subs_ops->lock(bes2600_sbus_priv); +#ifndef CONFIG_BES2600_WLAN_BES + { + int buf_id_rx = hw_priv->buf_id_rx; + while (retry <= MAX_RETRY) { + ret = __bes2600_reg_read(hw_priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + hw_priv->buf_id_rx = buf_id_rx; + break; + } else { + retry++; + mdelay(1); + bes2600_err(BES2600_DBG_SBUS, "%s,error :[%d]\n", + __func__, ret); + } + } + } +#else + while (retry <= MAX_RETRY) { + ret = bes2600_subs_ops->sbus_memcpy_fromio(bes2600_sbus_priv, + BES_TX_DATA_ADDR, buf, buf_len); + if (ret) { + retry ++; + mdelay(1); + bes2600_dbg(BES2600_DBG_SBUS, "%s error :[%d]\n", + __func__, ret); + } else { + break; + } + } +#endif + bes2600_subs_ops->unlock(bes2600_sbus_priv); + return ret; +} + +int bes2600_data_write(const void *buf, size_t buf_len) +{ + int ret, retry = 1; + u32 addr = 0; +#ifdef CONFIG_BES2600_WLAN_SPI + struct HI_MSG_HDR { + u16 MsgLen; + u16 MsgId; + } *pMsg; +#endif + + BUG_ON(!bes2600_subs_ops); + bes2600_subs_ops->lock(bes2600_sbus_priv); +#ifndef CONFIG_BES2600_WLAN_BES + { + int buf_id_tx = hw_priv->buf_id_tx; + while (retry <= MAX_RETRY) { + ret = __bes2600_reg_write(hw_priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + hw_priv->buf_id_tx = buf_id_tx; + break; + } else { + retry++; + mdelay(1); + bes2600_err(BES2600_DBG_SBUS, "%s,error :[%d]\n", + __func__, ret); + } + } + } +#else +#ifdef CONFIG_BES2600_WLAN_SPI + #define IS_DRIVER_VENDOR_CMD(X) ((X & 0x0C00) == 0x0C00) + pMsg = (struct HI_MSG_HDR *)buf; + if (IS_DRIVER_VENDOR_CMD(pMsg->MsgId)) { + addr = BES_MISC_DATA_ADDR; + pr_err("mcu message detected, %x\n", BES_MISC_DATA_ADDR); + } +#endif + while (retry <= MAX_RETRY) { + ret = bes2600_subs_ops->sbus_memcpy_toio(bes2600_sbus_priv, addr, buf, buf_len); + if (ret) { + retry++; + mdelay(1); + bes2600_dbg(BES2600_DBG_SBUS, "%s,error :[%d]\n", + __func__, ret); + } else { + break; + } + } +#endif + bes2600_subs_ops->unlock(bes2600_sbus_priv); + return ret; +} + +int bes2600_indirect_read(u32 addr, void *buf, size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't read more than 0xfff words.\n", + __func__); + WARN_ON(1); + return -EINVAL; + goto out; + } + + bes2600_subs_ops->lock(bes2600_sbus_priv); + /* Write address */ + ret = __bes2600_reg_write_32(ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __bes2600_reg_read_32(ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't read config register.\n", + __func__); + goto out; + } + + /* Set PREFETCH bit */ + ret = __bes2600_reg_write_32(ST90TDS_CONFIG_REG_ID, val32 | prefetch); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't write prefetch bit.\n", + __func__); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __bes2600_reg_read_32(ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't check prefetch bit.\n", + __func__); + goto out; + } + if (!(val32 & prefetch)) + break; + + mdelay(i); + } + + if (val32 & prefetch) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Prefetch bit is not cleared.\n", + __func__); + goto out; + } + + /* Read data port */ + ret = __bes2600_reg_read(port_addr, buf, buf_len, 0); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't read data port.\n", + __func__); + goto out; + } + +out: + bes2600_subs_ops->unlock(bes2600_sbus_priv); + return ret; +} + +int bes2600_apb_write(u32 addr, const void *buf, size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't wrire more than 0xfff words.\n", + __func__); + WARN_ON(1); + return -EINVAL; + } + + bes2600_subs_ops->lock(bes2600_sbus_priv); + + /* Write address */ + ret = __bes2600_reg_write_32(ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Write data port */ + ret = __bes2600_reg_write(ST90TDS_SRAM_DPORT_REG_ID, buf, buf_len, 0); + if (ret < 0) { + bes2600_err(BES2600_DBG_SBUS, "%s: Can't write data port.\n", + __func__); + goto out; + } + +out: + bes2600_subs_ops->unlock(bes2600_sbus_priv); + return ret; +} + +#if defined(BES2600_DETECTION_LOGIC) +int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len) +{ + int ret; + bes2600_info(BES2600_DBG_SBUS,"%s: ENTER\n",__func__); + if ((buf_len / 2) >= 0x1000) { + bes2600_dbg(BES2600_DBG_SBUS, + "%s: Can't wrire more than 0xfff words.\n", + __func__); + WARN_ON(1); + bes2600_info(BES2600_DBG_SBUS, "%s:EXIT (1) \n",__func__); + return -EINVAL; + } + + bes2600_subs_ops->lock(bes2600_sbus_priv); + + /* Write address */ + ret = __bes2600_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + bes2600_dbg(BES2600_DBG_SBUS, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Write data port */ + ret = __bes2600_reg_write(priv, ST90TDS_AHB_DPORT_REG_ID, + buf, buf_len, 0); + if (ret < 0) { + bes2600_dbg(BES2600_DBG_SBUS, "%s: Can't write data port.\n", + __func__); + goto out; + } + +out: + bes2600_subs_ops->unlock(priv->bes2600_sbus_priv); + return ret; +} +#endif + +int __bes2600_irq_enable(int enable) +{ + return 0; +} diff --git a/drivers/staging/bes2600/hwio.h b/drivers/staging/bes2600/hwio.h new file mode 100644 index 000000000000..aa29ca449ebd --- /dev/null +++ b/drivers/staging/bes2600/hwio.h @@ -0,0 +1,364 @@ +/* + * Low-level API for mac80211 BES2600 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * Based on: + * UMAC BES2600 driver which is + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_HWIO_H_INCLUDED +#define BES2600_HWIO_H_INCLUDED + +/* extern */ struct sbus_ops; +/* extern */ struct sbus_priv; + +/* DPLL initial values */ +#define DPLL_INIT_VAL_9000 (0x00000191) +#define DPLL_INIT_VAL_BES2600 (0x0EC4F121) + +/* Hardware Type Definitions */ +#define HIF_8601_VERSATILE (0) +#define HIF_8601_SILICON (1) +#define HIF_9000_SILICON_VERSTAILE (2) + +#define BES2600_CUT_11_ID_STR (0x302E3830) +#define BES2600_CUT_22_ID_STR1 (0x302e3132) +#define BES2600_CUT_22_ID_STR2 (0x32302e30) +#define BES2600_CUT_22_ID_STR3 (0x3335) +#define CW1250_CUT_11_ID_STR1 (0x302e3033) +#define CW1250_CUT_11_ID_STR2 (0x33302e32) +#define CW1250_CUT_11_ID_STR3 (0x3535) +#define BES2600_CUT_ID_ADDR (0xFFF17F90) +#define BES2600_CUT2_ID_ADDR (0xFFF1FF90) + +/* Download control area */ +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 ImageSize; + /* downloading flags */ + u32 Flags; + /* No. of bytes put into the download, init & updated by host */ + u32 Put; + /* last traced program counter, last ARM reg_pc */ + u32 TracePc; + /* No. of bytes read from the download, host init, device updates */ + u32 Get; + /* r0, boot losader status, host init to pending, device updates */ + u32 Status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData)) +#define DOWNLOAD_DEBUG_DATA_LEN (108) + +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + + +#define SYS_BASE_ADDR_SILICON (0) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) + +#define BES26000_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* *************************************************************** +*Device register definitions +*************************************************************** */ +/* WBF - SPI Register Addresses */ +#define ST90TDS_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define ST90TDS_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define ST90TDS_FRAME_OUT_REG_ID (0x0007) +#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) +#define ST90TDS_CONT_WUP_BIT (BIT(12)) +#define ST90TDS_CONT_RDY_BIT (BIT(13)) +#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) +#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) +#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) +#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) +#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) +#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) +#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) +#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) +#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define ST90TDS_CONFIG_AHB_PFETCH_BIT (BIT(11)) +#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define ST90TDS_CONFIG_PFETCH_BIT (BIT(13)) +/* cpu reset */ +#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) +#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For BES2600 the IRQ Enable and Ready Bits are in CONFIG register */ +#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +void bes2600_reg_set_object(struct sbus_ops *ops, struct sbus_priv *priv); +int bes2600_data_read(void *buf, size_t buf_len); +int bes2600_data_write(const void *buf, size_t buf_len); + +int bes2600_reg_read(u32 addr, void *buf, size_t buf_len); +int bes2600_reg_write(u32 addr, const void *buf, size_t buf_len); + +static inline int bes2600_reg_read_16(u16 addr, u16 *val) +{ + return bes2600_reg_read(addr, val, sizeof(*val)); +} + +static inline int bes2600_reg_write_16(u16 addr, u16 val) +{ + return bes2600_reg_write(addr, &val, sizeof(val)); +} + +static inline int bes2600_reg_read_32(u16 addr, u32 *val) +{ + return bes2600_reg_read(addr, val, sizeof(val)); +} + +static inline int bes2600_reg_write_32(u16 addr, u32 val) +{ + return bes2600_reg_write(addr, &val, sizeof(val)); +} + +int bes2600_indirect_read(u32 addr, void *buf, size_t buf_len, u32 prefetch, u16 port_addr); +int bes2600_apb_write(u32 addr, const void *buf, size_t buf_len); +int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len); + +static inline int bes2600_apb_read(u32 addr, void *buf, size_t buf_len) +{ + return bes2600_indirect_read(addr, buf, buf_len, + ST90TDS_CONFIG_PFETCH_BIT, ST90TDS_SRAM_DPORT_REG_ID); +} + +static inline int bes2600_ahb_read(u32 addr, void *buf, size_t buf_len) +{ + return bes2600_indirect_read(addr, buf, buf_len, + ST90TDS_CONFIG_AHB_PFETCH_BIT, ST90TDS_AHB_DPORT_REG_ID); +} + +static inline int bes2600_apb_read_32(u32 addr, u32 *val) +{ + return bes2600_apb_read(addr, val, sizeof(val)); +} + +static inline int bes2600_apb_write_32(u32 addr, u32 val) +{ + return bes2600_apb_write(addr, &val, sizeof(val)); +} + +static inline int bes2600_ahb_read_32(u32 addr, u32 *val) +{ + return bes2600_ahb_read(addr, val, sizeof(val)); +} + +#if defined(BES2600_DETECTION_LOGIC) +static inline int bes2600_ahb_write_32(u32 addr, u32 val) +{ + return bes2600_ahb_write(addr, &val, sizeof(val)); +} +#endif /*BES2600_DETECTION_LOGIC*/ + +#ifdef CONFIG_BES2600_WLAN_USB +#define BES_TX_DATA_ADDR (0x0) +#endif + +#ifdef CONFIG_BES2600_WLAN_SDIO +#define SDIO_DEVICE_SEND_INT_LEN_SEPARATE + +#define BES_TX_CTRL_REG_ID (0x0) + +#ifdef SDIO_DEVICE_SEND_INT_LEN_SEPARATE +#define BES_TX_NEXT_LEN_REG_ID (0x104) +#else +#define BES_TX_NEXT_LEN_REG_ID BES_TX_CTRL_REG_ID +#endif + +#define BES_TX_NEXT_LEN_MASK (0xffff) +#define BES_TX_DATA_ADDR (0x0) + +#define BES_HOST_INT_REG_ID (0x120) +#define BES_HOST_INT (1 << 0) +#define BES_AP_WAKEUP_CFG (1 << 1) +#define BES_SUBSYSTEM_MCU_DEACTIVE (1 << 2) +#define BES_SUBSYSTEM_MCU_ACTIVE (1 << 3) +#define BES_SUBSYSTEM_WIFI_DEACTIVE (1 << 4) +#define BES_SUBSYSTEM_WIFI_ACTIVE (1 << 5) +#define BES_SUBSYSTEM_WIFI_DEBUG (1 << 6) +#define BES_SUBSYSTEM_BT_DEACTIVE (1 << 7) +#define BES_SUBSYSTEM_BT_ACTIVE (1 << 8) +#define BES_SUBSYSTEM_SYSTEM_CLOSE (1 << 9) +#define BES_SUBSYSTEM_BT_WAKEUP (1 << 10) +#define BES_SUBSYSTEM_BT_SLEEP (1 << 11) + +#define BES_AP_WAKEUP_TYPE_MASK 0xC +#define BES_AP_WAKEUP_TYPE_SHIFT 2 +#define BES_AP_WAKEUP_TYPE_GPIO 0 +#define BES_AP_WAKEUP_TYPE_IF 1 + +#define BES_AP_WAKEUP_REG_ID (0x124) +#define BES_AP_WAKEUP_CFG_VALID (0x80) + +#define BES_AP_WAKEUP_GPIO_MASK (0x3) +#define BES_AP_WAKEUP_GPIO_HIGH (0x0) +#define BES_AP_WAKEUP_GPIO_LOW (0x1) +#define BES_AP_WAKEUP_GPIO_RISE (0x2) +#define BES_AP_WAKEUP_GPIO_FALL (0x3) + +#define BES_SLAVE_STATUS_REG_ID (0x10c) +#define BES_SLAVE_STATUS_MCU_READY (1 << 0) +#define BES_SLAVE_STATUS_DPD_READY (1 << 1) +#define BES_SLAVE_STATUS_WIFI_READY (1 << 2) +#define BES_SLAVE_STATUS_BT_READY (1 << 3) +#define BES_SLAVE_STATUS_MCU_WAKEUP_READY (1 << 4) +#define BES_SLAVE_STATUS_BT_WAKE_READY (1 << 5) +#define BES_SLAVE_STATUS_DPD_LOG_READY (1 << 6) + +#define PACKET_TOTAL_LEN(len) ((len) & 0xffff) +#define PACKET_COUNT(len) (((len) >> 16) & 0xff) +#define PAKCET_CRC8(len) (((len) >> 24) & 0xff) + +#define BES_SDIO_RX_MULTIPLE_NUM (16) +#define BES_SDIO_TX_MULTIPLE_NUM (16) +#define BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL (1) + +#define MAX_SDIO_TRANSFER_LEN (32768) +#endif + +#ifdef CONFIG_BES2600_WLAN_SPI + +#define SPI_RD_CFG_REG_ID (0xfffc0004) +#define SPI_NCONTINUOUS_CFG_VAL (0x0) +#define SPI_CONTINUOUS_CFG_VAL (0x1) +#define SPI_RD_ADDR(X) ((X >> 2) | (1 << 30)) +#define SPI_WR_ADDR(X) ((X >> 2) | (0 << 30)) + +#define SPI_MASTER_SECTION_BASE (0x20083000) + +#define BES_HOST_SYNC_REG_ID (SPI_MASTER_SECTION_BASE) +#define BES_SLAVE_SYNC_HEADER (0xbe572002) + +#define BES_HOST_SUBINT_REG_ID (SPI_MASTER_SECTION_BASE + 0x4) +#define BES_SLAVE_STATUS_REG_ID (SPI_MASTER_SECTION_BASE + 0X8) + +#define BES_LMAC_BUF_NUMS (64) +#define BES_LMAC_BUF_TOTAL (SPI_MASTER_SECTION_BASE + 0xC) + +#define BES_TX_CTRL_REG_ID (SPI_MASTER_SECTION_BASE + 0x10) +#define BES_TX_NEXT_LEN_MASK (0xffffffff) + +#define BES_LMAC_BUF_DESC (SPI_MASTER_SECTION_BASE + 0x14) + +#define BES_TX_DATA_ADDR (SPI_MASTER_SECTION_BASE + 0x114) + +#define MAX_SEND_PACKETS_NUM (8) +#define BES_MISC_DATA_ADDR (BES_TX_DATA_ADDR + (1632 + 4 + 4) * MAX_SEND_PACKETS_NUM) + +#define BES_CALI_DATA_ADDR (0x2008c000) +#define BES_FACTORY_DATA_ADDR (0x2008b000) + + +#define BES_HOST_INT_REG_ID (0x40000098) +#define BES_HOST_INT_RD_DONE (1 << 0) +#define BES_HOST_INT_WR_DONE (1 << 1) +#define BES_HOST_INT_SUBINT (1 << 2) + +#define BES_AP_WAKEUP_CFG (1 << 1) +#define BES_SUBSYSTEM_MCU_DEACTIVE (1 << 2) +#define BES_SUBSYSTEM_MCU_ACTIVE (1 << 3) +#define BES_SUBSYSTEM_WIFI_DEACTIVE (1 << 4) +#define BES_SUBSYSTEM_WIFI_ACTIVE (1 << 5) +#define BES_SUBSYSTEM_WIFI_DEBUG (1 << 6) +#define BES_SUBSYSTEM_BT_DEACTIVE (1 << 7) +#define BES_SUBSYSTEM_BT_ACTIVE (1 << 8) +#define BES_SUBSYSTEM_SYSTEM_CLOSE (1 << 9) +#define BES_DLD_FACTORY_DATA_DONE (1 << 12) +#define BES_DLD_DPD_DATA_DONE (1 << 13) +#define BES_MISC_DATA_DONE (1 << 14) + +#define BES_SLAVE_STATUS_MCU_READY (1 << 0) +#define BES_SLAVE_STATUS_WIFI_CALI_READY (1 << 1) +#define BES_SLAVE_STATUS_WIFI_OPEN_READY (1 << 2) +#define BES_SLAVE_STATUS_BT_OPEN_READY (1 << 3) +#define BES_SLAVE_STATUS_MCU_WAKEUP_READY (1 << 4) + +#define PACKET_TOTAL_LEN_V2(len) ((len) & 0xfffffff) +#define PACKET_COUNT_V2(len) (((len) >> 28) & 0xf) + +#endif + +#endif /* BES2600_HWIO_H_INCLUDED */ diff --git a/drivers/staging/bes2600/itp.c b/drivers/staging/bes2600/itp.c new file mode 100644 index 000000000000..e5c2958b5b96 --- /dev/null +++ b/drivers/staging/bes2600/itp.c @@ -0,0 +1,744 @@ +/* + * mac80211 glue code for mac80211 BES2600 drivers + * ITP code + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "bes2600.h" +#include "debug.h" +#include "itp.h" +#include "sta.h" + +static int __bes2600_itp_open(struct bes2600_common *priv); +static int __bes2600_itp_close(struct bes2600_common *priv); +static void bes2600_itp_rx_start(struct bes2600_common *priv); +static void bes2600_itp_rx_stop(struct bes2600_common *priv); +static void bes2600_itp_rx_stats(struct bes2600_common *priv); +static void bes2600_itp_rx_reset(struct bes2600_common *priv); +static void bes2600_itp_tx_stop(struct bes2600_common *priv); +static void bes2600_itp_handle(struct bes2600_common *priv, + struct sk_buff *skb); +static void bes2600_itp_err(struct bes2600_common *priv, + int err, + int arg); +static void __bes2600_itp_tx_stop(struct bes2600_common *priv); + +static ssize_t bes2600_itp_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *priv = file->private_data; + struct bes2600_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + int ret; + + if (skb_queue_empty(&itp->log_queue)) + return 0; + + skb = skb_dequeue(&itp->log_queue); + ret = copy_to_user(user_buf, skb->data, skb->len); + *ppos += skb->len; + skb->data[skb->len] = 0; + bes2600_dbg(BES2600_DBG_ITP, "[ITP] >>> %s", skb->data); + consume_skb(skb); + + return skb->len - ret; +} + +static ssize_t bes2600_itp_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct bes2600_common *priv = file->private_data; + struct sk_buff *skb; + + if (!count || count > 1024) + return -EINVAL; + skb = dev_alloc_skb(count + 1); + if (!skb) + return -ENOMEM; + skb_trim(skb, 0); + skb_put(skb, count + 1); + if (copy_from_user(skb->data, user_buf, count)) { + kfree_skb(skb); + return -EFAULT; + } + skb->data[count] = 0; + + bes2600_itp_handle(priv, skb); + consume_skb(skb); + return count; +} + +static unsigned int bes2600_itp_poll(struct file *file, poll_table *wait) +{ + struct bes2600_common *priv = file->private_data; + struct bes2600_itp *itp = &priv->debug->itp; + unsigned int mask = 0; + + poll_wait(file, &itp->read_wait, wait); + + if (!skb_queue_empty(&itp->log_queue)) + mask |= POLLIN | POLLRDNORM; + + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +static int bes2600_itp_open(struct inode *inode, struct file *file) +{ + struct bes2600_common *priv = inode->i_private; + struct bes2600_itp *itp = &priv->debug->itp; + int ret = 0; + + file->private_data = priv; + if (atomic_inc_return(&itp->open_count) == 1) { + ret = __bes2600_itp_open(priv); + if (ret && !atomic_dec_return(&itp->open_count)) + __bes2600_itp_close(priv); + } else { + atomic_dec(&itp->open_count); + ret = -EBUSY; + } + + return ret; +} + +static int bes2600_itp_close(struct inode *inode, struct file *file) +{ + struct bes2600_common *priv = file->private_data; + struct bes2600_itp *itp = &priv->debug->itp; + if (!atomic_dec_return(&itp->open_count)) { + __bes2600_itp_close(priv); + wake_up(&itp->close_wait); + } + return 0; +} + +static const struct file_operations fops_itp = { + .open = bes2600_itp_open, + .read = bes2600_itp_read, + .write = bes2600_itp_write, + .poll = bes2600_itp_poll, + .release = bes2600_itp_close, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void bes2600_itp_fill_pattern(u8 *data, int size, + enum bes2600_itp_data_modes mode) +{ + u8 *p = data; + + if (size <= 0) + return; + + switch (mode) { + default: + case ITP_DATA_ZEROS: + memset(data, 0x0, size); + break; + case ITP_DATA_ONES: + memset(data, 0xff, size); + break; + case ITP_DATA_ZERONES: + memset(data, 0x55, size); + break; + case ITP_DATA_RANDOM: + while (p < data+size-sizeof(u32)) { + (*(u32 *)p) = random32(); + p += sizeof(u32); + } + while (p < data+size) { + (*p) = random32() & 0xFF; + p++; + } + break; + } + return; +} + +static void bes2600_itp_tx_work(struct work_struct *work) +{ + struct bes2600_itp *itp = container_of(work, struct bes2600_itp, + tx_work.work); + struct bes2600_common *priv = itp->priv; + atomic_set(&priv->bh_tx, 1); + wake_up(&priv->bh_wq); +} + +static void bes2600_itp_tx_finish(struct work_struct *work) +{ + struct bes2600_itp *itp = container_of(work, struct bes2600_itp, + tx_finish.work); + __bes2600_itp_tx_stop(itp->priv); +} + +int bes2600_itp_init(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + + itp->priv = priv; + atomic_set(&itp->open_count, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&itp->awaiting_confirm, 0); + skb_queue_head_init(&itp->log_queue); + spin_lock_init(&itp->tx_lock); + init_waitqueue_head(&itp->read_wait); + init_waitqueue_head(&itp->write_wait); + init_waitqueue_head(&itp->close_wait); + INIT_DELAYED_WORK(&itp->tx_work, bes2600_itp_tx_work); + INIT_DELAYED_WORK(&itp->tx_finish, bes2600_itp_tx_finish); + itp->data = NULL; + itp->hdr_len = WSM_TX_EXTRA_HEADROOM + + sizeof(struct ieee80211_hdr_3addr); + itp->id = 0; + + if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, + priv->debug->debugfs_phy, priv, &fops_itp)) + return -ENOMEM; + + return 0; +} + +void bes2600_itp_release(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + + wait_event_interruptible(itp->close_wait, + !atomic_read(&itp->open_count)); + + WARN_ON(atomic_read(&itp->open_count)); + + skb_queue_purge(&itp->log_queue); + bes2600_itp_tx_stop(priv); +} + +static int __bes2600_itp_open(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + + if (!priv->vif) + return -EINVAL; + if (priv->join_status) + return -EINVAL; + itp->saved_channel = priv->channel; + if (!priv->channel) + priv->channel = &priv->hw-> + wiphy->bands[NL80211_BAND_2GHZ]->channels[0]; + wsm_set_bssid_filtering(priv, false); + bes2600_itp_rx_reset(priv); + return 0; +} + +static int __bes2600_itp_close(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) + bes2600_itp_rx_stop(priv); + bes2600_itp_tx_stop(priv); + bes2600_disable_listening(priv); + bes2600_update_filtering(priv); + priv->channel = itp->saved_channel; + return 0; +} + +bool bes2600_is_itp(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + return atomic_read(&itp->open_count) != 0; +} + +static void bes2600_itp_rx_reset(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; +} + +static void bes2600_itp_rx_start(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + + bes2600_dbg(BES2600_DBG_ITP, "[ITP] RX start, band = %d, ch = %d\n", + itp->band, itp->ch); + atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); + bes2600_disable_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + bes2600_enable_listening(priv, priv->channel); + wsm_set_bssid_filtering(priv, false); +} + +static void bes2600_itp_rx_stop(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + bes2600_dbg(BES2600_DBG_ITP, "[ITP] RX stop\n"); + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + bes2600_itp_rx_reset(priv); +} + +static void bes2600_itp_rx_stats(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[128]; + int len, ret; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + + if (ret) + bes2600_itp_err(priv, -EBUSY, 20); + + if (!itp->rx_cnt) + len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", + counters.countRxPacketErrors); + else + len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", + itp->rx_cnt, + itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, + itp->rx_rssi_min, itp->rx_rssi_max, + counters.countRxPacketErrors); + + if (len <= 0) { + bes2600_itp_err(priv, -EBUSY, 21); + return; + } + + skb = dev_alloc_skb(len); + if (!skb) { + bes2600_itp_err(priv, -ENOMEM, 22); + return; + } + + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +static void bes2600_itp_tx_start(struct bes2600_common *priv) +{ + struct wsm_tx *tx; + struct ieee80211_hdr_3addr *hdr; + struct bes2600_itp *itp = &priv->debug->itp; + struct wsm_association_mode assoc_mode = { + .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, + .preambleType = itp->preamble, + }; + int len; + u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; + + /* Rates index 4 and 5 are not supported */ + if (itp->rate > 3) + itp->rate += 2; + + bes2600_dbg(BES2600_DBG_ITP, "[ITP] TX start: band = %d, ch = %d, rate = %d," + " preamble = %d, number = %d, data_mode = %d," + " interval = %d, power = %d, data_len = %d\n", + itp->band, itp->ch, itp->rate, itp->preamble, + itp->number, itp->data_mode, itp->interval_us, + itp->power, itp->data_len); + + len = itp->hdr_len + itp->data_len; + + itp->data = kmalloc(len, GFP_KERNEL); + tx = (struct wsm_tx *)itp->data; + tx->hdr.len = itp->data_len + itp->hdr_len; + tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); + tx->maxTxRate = itp->rate; + tx->queueId = 3; + tx->more = 0; + tx->flags = 0xc; + tx->packetID = 0; + tx->reserved = 0; + tx->expireTime = 0; + + if (itp->preamble == ITP_PREAMBLE_GREENFIELD) + tx->htTxParameters = WSM_HT_TX_GREENFIELD; + else if (itp->preamble == ITP_PREAMBLE_MIXED) + tx->htTxParameters = WSM_HT_TX_MIXED; + + hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; + memset(hdr, 0, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_FCTL_TODS); + memcpy(hdr->addr1, da_addr, ETH_ALEN); + memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(hdr->addr3, da_addr, ETH_ALEN); + + bes2600_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + bes2600_disable_listening(priv); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + WARN_ON(wsm_set_output_power(priv, itp->power)); + if (itp->preamble == ITP_PREAMBLE_SHORT || + itp->preamble == ITP_PREAMBLE_LONG) + WARN_ON(wsm_set_association_mode(priv, + &assoc_mode)); + wsm_set_bssid_filtering(priv, false); + bes2600_enable_listening(priv, priv->channel); + + spin_lock_bh(&itp->tx_lock); + atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); + atomic_set(&itp->awaiting_confirm, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + wake_up(&priv->bh_wq); + spin_unlock_bh(&itp->tx_lock); +} + +void __bes2600_itp_tx_stop(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + spin_lock_bh(&itp->tx_lock); + kfree(itp->data); + itp->data = NULL; + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + spin_unlock_bh(&itp->tx_lock); +} + +static void bes2600_itp_tx_stop(struct bes2600_common *priv) +{ + struct bes2600_itp *itp = &priv->debug->itp; + bes2600_dbg(BES2600_DBG_ITP, "[ITP] TX stop\n"); + atomic_set(&itp->stop_tx, 1); + flush_workqueue(priv->workqueue); + + /* time for FW to confirm all tx requests */ + msleep(500); + + __bes2600_itp_tx_stop(priv); +} + +static void bes2600_itp_get_version(struct bes2600_common *priv, + enum bes2600_itp_version_type type) +{ + struct bes2600_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[ITP_BUF_SIZE]; + size_t size = 0; + int len; + bes2600_dbg(BES2600_DBG_ITP, "[ITP] print %s version\n", type == ITP_CHIP_ID ? + "chip" : "firmware"); + + len = snprintf(buf, ITP_BUF_SIZE, "2,"); + if (len <= 0) { + bes2600_itp_err(priv, -EINVAL, 40); + return; + } + size += len; + + switch (type) { + case ITP_CHIP_ID: + len = bes2600_print_fw_version(priv, buf+size, + ITP_BUF_SIZE - size); + + if (len <= 0) { + bes2600_itp_err(priv, -EINVAL, 41); + return; + } + size += len; + break; + case ITP_FW_VER: + len = snprintf(buf+size, ITP_BUF_SIZE - size, + "%d.%d", priv->wsm_caps.hardwareId, + priv->wsm_caps.hardwareSubId); + if (len <= 0) { + bes2600_itp_err(priv, -EINVAL, 42); + return; + } + size += len; + break; + default: + bes2600_itp_err(priv, -EINVAL, 43); + break; + } + + len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); + if (len <= 0) { + bes2600_itp_err(priv, -EINVAL, 44); + return; + } + size += len; + + skb = dev_alloc_skb(size); + if (!skb) { + bes2600_itp_err(priv, -ENOMEM, 45); + return; + } + + skb_trim(skb, 0); + skb_put(skb, size); + + memcpy(skb->data, buf, size); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct bes2600_itp *itp; + struct wsm_tx *tx; + struct timespec now; + int time_left_us; + + if (!priv->debug) + return 0; + + itp = &priv->debug->itp; + + if (!itp) + return 0; + + spin_lock_bh(&itp->tx_lock); + if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) + goto out; + + if (atomic_read(&itp->stop_tx)) + goto out; + + if (itp->number == 0) { + atomic_set(&itp->stop_tx, 1); + queue_delayed_work(priv->workqueue, &itp->tx_finish, + HZ/10); + goto out; + } + + if (!itp->data) + goto out; + + if (priv->hw_bufs_used >= 2) { + if (!atomic_read(&priv->bh_rx)) + atomic_set(&priv->bh_rx, 1); + atomic_set(&priv->bh_tx, 1); + goto out; + } + + ktime_get_ts(&now); + time_left_us = (itp->last_sent.tv_sec - + now.tv_sec)*1000000 + + (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + + itp->interval_us; + + if (time_left_us > ITP_TIME_THRES_US) { + queue_delayed_work(priv->workqueue, &itp->tx_work, + ITP_US_TO_MS(time_left_us)*HZ/1000); + goto out; + } + + if (time_left_us > 50) + udelay(time_left_us); + + if (itp->number > 0) + itp->number--; + + *data = itp->data; + *tx_len = itp->data_len + itp->hdr_len; + + if (itp->data_mode == ITP_DATA_RANDOM) + bes2600_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + tx = (struct wsm_tx *)itp->data; + tx->packetID = __cpu_to_le32(itp->id++); + *burst = 2; + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + atomic_add(1, &itp->awaiting_confirm); + spin_unlock_bh(&itp->tx_lock); + return 1; + +out: + spin_unlock_bh(&itp->tx_lock); + return 0; +} + +bool bes2600_itp_rxed(struct bes2600_common *priv, struct sk_buff *skb) +{ + struct bes2600_itp *itp = &priv->debug->itp; + struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); + int signal; + + if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) + return bes2600_is_itp(priv); + if (rx->freq != priv->channel->center_freq) + return true; + + signal = rx->signal; + itp->rx_cnt++; + itp->rx_rssi += signal; + if (itp->rx_rssi_min > rx->signal) + itp->rx_rssi_min = rx->signal; + if (itp->rx_rssi_max < rx->signal) + itp->rx_rssi_max = rx->signal; + + return true; +} + +void bes2600_itp_wake_up_tx(struct bes2600_common *priv) +{ + wake_up(&priv->debug->itp.write_wait); +} + +bool bes2600_itp_tx_running(struct bes2600_common *priv) +{ + if (atomic_read(&priv->debug->itp.awaiting_confirm) || + atomic_read(&priv->debug->itp.test_mode) == + TEST_MODE_TX_TEST) { + atomic_sub(1, &priv->debug->itp.awaiting_confirm); + return true; + } + return false; +} + +static void bes2600_itp_handle(struct bes2600_common *priv, + struct sk_buff *skb) +{ + struct bes2600_itp *itp = &priv->debug->itp; + const struct wiphy *wiphy = priv->hw->wiphy; + int cmd; + int ret; + + bes2600_dbg(BES2600_DBG_ITP, "[ITP] <<< %s", skb->data); + if (sscanf(skb->data, "%d", &cmd) != 1) { + bes2600_itp_err(priv, -EINVAL, 1); + return; + } + + switch (cmd) { + case 1: /* RX test */ + if (atomic_read(&itp->test_mode)) { + bes2600_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d", + &cmd, &itp->band, &itp->ch); + if (ret != 3) { + bes2600_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + bes2600_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + bes2600_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + bes2600_itp_err(priv, -EINVAL, 3); + else { + bes2600_itp_rx_stats(priv); + bes2600_itp_rx_start(priv); + } + break; + case 2: /* RX stat */ + bes2600_itp_rx_stats(priv); + break; + case 3: /* RX/TX stop */ + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { + bes2600_itp_rx_stats(priv); + bes2600_itp_rx_stop(priv); + } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { + bes2600_itp_tx_stop(priv); + } else + bes2600_itp_err(priv, -EBUSY, 0); + break; + case 4: /* TX start */ + if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { + bes2600_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &cmd, &itp->band, &itp->ch, &itp->rate, + &itp->preamble, &itp->number, &itp->data_mode, + &itp->interval_us, &itp->power, &itp->data_len); + if (ret != 10) { + bes2600_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + bes2600_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + bes2600_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + bes2600_itp_err(priv, -EINVAL, 3); + else if (itp->rate >= 20) + bes2600_itp_err(priv, -EINVAL, 4); + else if (itp->preamble >= ITP_PREAMBLE_MAX) + bes2600_itp_err(priv, -EINVAL, 5); + else if (itp->data_mode >= ITP_DATA_MAX_MODE) + bes2600_itp_err(priv, -EINVAL, 7); + else if (itp->data_len < ITP_MIN_DATA_SIZE || + itp->data_len > priv->wsm_caps.sizeInpChBuf - + itp->hdr_len) + bes2600_itp_err(priv, -EINVAL, 8); + else { + bes2600_itp_tx_start(priv); + } + break; + case 5: + bes2600_itp_get_version(priv, ITP_CHIP_ID); + break; + case 6: + bes2600_itp_get_version(priv, ITP_FW_VER); + break; + + } +} + +static void bes2600_itp_err(struct bes2600_common *priv, + int err, int arg) +{ + struct bes2600_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + static char buf[255]; + int len; + + len = snprintf(buf, sizeof(buf), "%d,%d\n", + err, arg); + if (len <= 0) + return; + + skb = dev_alloc_skb(len); + if (!skb) + return; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); + + len = sprint_symbol(buf, + (unsigned long)__builtin_return_address(0)); + if (len <= 0) + return; + bes2600_dbg(BES2600_DBG_ITP, "[ITP] error %d,%d from %s\n", + err, arg, buf); +} diff --git a/drivers/staging/bes2600/itp.h b/drivers/staging/bes2600/itp.h new file mode 100644 index 000000000000..5cfba46896eb --- /dev/null +++ b/drivers/staging/bes2600/itp.h @@ -0,0 +1,152 @@ +/* + * ITP code for BES2600 mac80211 driver + * + * Copyright (c) 2011, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_ITP_H_INCLUDED +#define BES2600_ITP_H_INCLUDED + +struct cw200_common; +struct wsm_tx_confirm; +struct dentry; + +#ifdef CONFIG_BES2600_ITP + +/*extern*/ struct ieee80211_channel; + +#define TEST_MODE_NO_TEST (0) +#define TEST_MODE_RX_TEST (1) +#define TEST_MODE_TX_TEST (2) + +#define itp_printk(...) printk(__VA_ARGS__) +#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +#define ITP_MIN_DATA_SIZE 6 +#define ITP_MAX_DATA_SIZE 1600 +#define ITP_TIME_THRES_US 10000 +#define ITP_US_TO_MS(x) ((x)/1000) +#define ITP_MS_TO_US(x) ((x)*1000) +#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1 +#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\ +precision with current HZ value ! +#endif +#define ITP_BUF_SIZE 255 + + +enum bes2600_itp_data_modes { + ITP_DATA_ZEROS, + ITP_DATA_ONES, + ITP_DATA_ZERONES, + ITP_DATA_RANDOM, + ITP_DATA_MAX_MODE, +}; + +enum bes2600_itp_version_type { + ITP_CHIP_ID, + ITP_FW_VER, +}; + +enum bes2600_itp_preamble_type { + ITP_PREAMBLE_LONG, + ITP_PREAMBLE_SHORT, + ITP_PREAMBLE_OFDM, + ITP_PREAMBLE_MIXED, + ITP_PREAMBLE_GREENFIELD, + ITP_PREAMBLE_MAX, +}; + + +struct bes2600_itp { + struct bes2600_common *priv; + atomic_t open_count; + atomic_t awaiting_confirm; + struct sk_buff_head log_queue; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t close_wait; + struct ieee80211_channel *saved_channel; + atomic_t stop_tx; + struct delayed_work tx_work; + struct delayed_work tx_finish; + spinlock_t tx_lock; + struct timespec last_sent; + atomic_t test_mode; + int rx_cnt; + long rx_rssi; + int rx_rssi_max; + int rx_rssi_min; + unsigned band; + unsigned ch; + unsigned rate; + unsigned preamble; + unsigned int number; + unsigned data_mode; + int interval_us; + int power; + u8 *data; + int hdr_len; + int data_len; + int id; +}; + +int bes2600_itp_init(struct bes2600_common *priv); +void bes2600_itp_release(struct bes2600_common *priv); + +bool bes2600_is_itp(struct bes2600_common *priv); +bool bes2600_itp_rxed(struct bes2600_common *priv, struct sk_buff *skb); +void bes2600_itp_wake_up_tx(struct bes2600_common *priv); +int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data, + size_t *tx_len, int *burst); +bool bes2600_itp_tx_running(struct bes2600_common *priv); + +#else /* CONFIG_BES2600_ITP */ + +static inline int +bes2600_itp_init(struct bes2600_common *priv) +{ + return 0; +} + +static inline void bes2600_itp_release(struct bes2600_common *priv) +{ +} + +static inline bool bes2600_is_itp(struct bes2600_common *priv) +{ + return false; +} + +static inline bool bes2600_itp_rxed(struct bes2600_common *priv, + struct sk_buff *skb) +{ + return false; +} + + +static inline void bes2600_itp_consume_txed(struct bes2600_common *priv) +{ +} + +static inline void bes2600_itp_wake_up_tx(struct bes2600_common *priv) +{ +} + +static inline int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + return 0; +} + +static inline bool bes2600_itp_tx_running(struct bes2600_common *priv) +{ + return false; +} + +#endif /* CONFIG_BES2600_ITP */ + +#endif /* BES2600_ITP_H_INCLUDED */ diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c new file mode 100644 index 000000000000..675839d7177a --- /dev/null +++ b/drivers/staging/bes2600/main.c @@ -0,0 +1,1101 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/*Linux version 3.4.0 compilation*/ +//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +#include +//#endif +#include +#include +#include +#include +#include +#include +#include + +#include "bes2600.h" +#include "txrx.h" +#include "sbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "ap.h" +#include "scan.h" +#include "debug.h" +#include "pm.h" +#include "bes2600_cfgvendor.h" +#include "bes2600_driver_mode.h" +#include "bes2600_factory.h" +#include "bes_chardev.h" +#include "txrx_opt.h" + +#ifdef PLAT_ROCKCHIP +#include +#endif + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("Softmac BES2600 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("bes2600"); + +static u8 bes2600_mac_template[ETH_ALEN] = { +#if (GET_MAC_ADDR_METHOD == 2)||(GET_MAC_ADDR_METHOD == 3) + 0x00, 0x12, 0x34, 0x00, 0x00, 0x00 +#else + 0x02, 0x80, 0xe1, 0x00, 0x00, 0x00 /* To use macaddr of customers */ +#endif +}; + +#if (GET_MAC_ADDR_METHOD == 2) /* To use macaddr and PS Mode of customers */ +#ifndef PATH_WIFI_MACADDR +#define PATH_WIFI_MACADDR "/efs/wifi/.mac.info" +#endif +#elif (GET_MAC_ADDR_METHOD == 3) +#define PATH_WIFI_MACADDR_TMP "/data/.mac.info" +#endif + +#ifdef CUSTOM_FEATURE +#define PATH_WIFI_PSM_INFO "/data/.psm.info" +static int savedpsm = 0; +#endif + +#if defined(CUSTOM_FEATURE) ||(GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) +int access_file(char *path, char *buffer, int size, int isRead); +#endif + +/* TODO: use rates and channels from the device */ +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate bes2600_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate bes2600_mcs_rates[] = { + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), +}; + +#define bes2600_a_rates (bes2600_rates + 4) +#define bes2600_a_rates_size (ARRAY_SIZE(bes2600_rates) - 4) +#define bes2600_g_rates (bes2600_rates + 0) +#define bes2600_g_rates_size (ARRAY_SIZE(bes2600_rates)) +#define bes2600_n_rates (bes2600_mcs_rates) +#define bes2600_n_rates_size (ARRAY_SIZE(bes2600_mcs_rates)) + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = NL80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel bes2600_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +#ifdef CONFIG_BES2600_5GHZ_SUPPORT +#if 0 +static struct ieee80211_channel bes2600_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; +#else +/* comply with china regulation on 5G */ +static struct ieee80211_channel bes2600_5ghz_chantable[] = { + CHAN5G(36, 0), + CHAN5G(40, 0), + CHAN5G(44, 0), + CHAN5G(48, 0), + CHAN5G(52, 0), + CHAN5G(56, 0), + CHAN5G(60, 0), + CHAN5G(64, 0), + CHAN5G(149, 0), + CHAN5G(153, 0), + CHAN5G(157, 0), + CHAN5G(161, 0), + CHAN5G(165, 0), +}; +#endif +#endif + +static struct ieee80211_supported_band bes2600_band_2ghz = { + .channels = bes2600_2ghz_chantable, + .n_channels = ARRAY_SIZE(bes2600_2ghz_chantable), + .bitrates = bes2600_g_rates, + .n_bitrates = bes2600_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +#ifdef CONFIG_BES2600_5GHZ_SUPPORT +static struct ieee80211_supported_band bes2600_band_5ghz = { + .channels = bes2600_5ghz_chantable, + .n_channels = ARRAY_SIZE(bes2600_5ghz_chantable), + .bitrates = bes2600_a_rates, + .n_bitrates = bes2600_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; +#endif /* CONFIG_BES2600_5GHZ_SUPPORT */ + +static const unsigned long bes2600_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + +static const struct ieee80211_iface_limit bes2600_if_limits[] = { + { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) }, +#ifdef P2P_MULTIVIF + { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, +#endif +}; + + + +static const struct ieee80211_iface_combination bes2600_if_comb[] = { + { + .limits = bes2600_if_limits, + .n_limits = ARRAY_SIZE(bes2600_if_limits), + .max_interfaces = CW12XX_MAX_VIFS, + .num_different_channels = 1, + }, +}; + + +static const struct ieee80211_ops bes2600_ops = { + .start = bes2600_start, + .stop = bes2600_stop, + .add_interface = bes2600_add_interface, + .remove_interface = bes2600_remove_interface, + .change_interface = bes2600_change_interface, + .tx = bes2600_tx, + .hw_scan = bes2600_hw_scan, + .cancel_hw_scan = bes2600_cancel_hw_scan, +#ifdef ROAM_OFFLOAD + .sched_scan_start = bes2600_hw_sched_scan_start, + .sched_scan_stop = bes2600_hw_sched_scan_stop, +#endif /*ROAM_OFFLOAD*/ + .set_tim = bes2600_set_tim, + .sta_notify = bes2600_sta_notify, + .sta_add = bes2600_sta_add, + .sta_remove = bes2600_sta_remove, + .set_key = bes2600_set_key, + .set_rts_threshold = bes2600_set_rts_threshold, + .config = bes2600_config, + .bss_info_changed = bes2600_bss_info_changed, + .prepare_multicast = bes2600_prepare_multicast, + .configure_filter = bes2600_configure_filter, + .conf_tx = bes2600_conf_tx, + .get_stats = bes2600_get_stats, + .ampdu_action = bes2600_ampdu_action, + .flush = bes2600_flush, +#ifdef CONFIG_PM + .suspend = bes2600_wow_suspend, + .resume = bes2600_wow_resume, +#endif + /* Intentionally not offloaded: */ + /*.channel_switch = bes2600_channel_switch, */ + .remain_on_channel = bes2600_remain_on_channel, + .cancel_remain_on_channel = bes2600_cancel_remain_on_channel, +#ifdef IPV6_FILTERING + //.set_data_filter = bes2600_set_data_filter, +#endif /*IPV6_FILTERING*/ +#ifdef CONFIG_BES2600_TESTMODE + .testmode_cmd = bes2600_testmode_cmd, +#endif +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support bes2600_wowlan_support = { + /* Support only for limited wowlan functionalities */ + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + +#ifdef CONFIG_BES2600_WAPI_SUPPORT +static void bes2600_init_wapi_cipher(struct ieee80211_hw *hw) +{ + static struct ieee80211_cipher_scheme wapi_cs = { + .cipher = WLAN_CIPHER_SUITE_SMS4, + .iftype = BIT(NL80211_IFTYPE_STATION), + .hdr_len = 18, + .pn_len = 16, + .pn_off = 2, + .key_idx_off = 0, + .key_idx_mask = 0x01, + .key_idx_shift = 0, + .mic_len = 16 + }; + + hw->cipher_schemes = &wapi_cs; + hw->n_cipher_schemes = 1; +} +#endif + +static void bes2600_get_base_mac(struct bes2600_common *hw_priv) +{ +#if (GET_MAC_ADDR_METHOD == 1) + u8 fixed_mac[ETH_ALEN]; +#endif +#if (GET_MAC_ADDR_METHOD == 2)||(GET_MAC_ADDR_METHOD == 3) /* To use macaddr of customers */ + char readmac[17+1]={0,}; +#endif + memcpy(hw_priv->addresses[0].addr, bes2600_mac_template, ETH_ALEN); + +#if (GET_MAC_ADDR_METHOD == 1) + rockchip_wifi_mac_addr(fixed_mac); + memcpy(hw_priv->addresses[0].addr, fixed_mac, ETH_ALEN * sizeof(u8)); + bes2600_info(BES2600_DBG_INIT, "get fixed mac address from flash=[%02x:%02x:%02x:%02x:%02x:%02x]\n", fixed_mac[0], fixed_mac[1], + fixed_mac[2], fixed_mac[3], fixed_mac[4], fixed_mac[5]); + if(fixed_mac[0] & (0x01)){ + bes2600_warn(BES2600_DBG_INIT, "The MAC address is not suitable for unicast, change to random MAC\n"); + memcpy(hw_priv->addresses[0].addr, bes2600_mac_template, ETH_ALEN); + } + +#elif (GET_MAC_ADDR_METHOD == 2) /* To use macaddr of customers */ + if(access_file(PATH_WIFI_MACADDR,readmac,17,1) > 0) { + sscanf(readmac,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + (u8 *)&hw_priv->addresses[0].addr[0], + (u8 *)&hw_priv->addresses[0].addr[1], + (u8 *)&hw_priv->addresses[0].addr[2], + (u8 *)&hw_priv->addresses[0].addr[3], + (u8 *)&hw_priv->addresses[0].addr[4], + (u8 *)&hw_priv->addresses[0].addr[5]); + } +#elif (GET_MAC_ADDR_METHOD == 3) + if(access_file(PATH_WIFI_MACADDR_TMP,readmac,17,1) > 0) { + sscanf(readmac,"%02X:%02X:%02X:%02X:%02X:%02X", + (u8 *)&hw_priv->addresses[0].addr[0], + (u8 *)&hw_priv->addresses[0].addr[1], + (u8 *)&hw_priv->addresses[0].addr[2], + (u8 *)&hw_priv->addresses[0].addr[3], + (u8 *)&hw_priv->addresses[0].addr[4], + (u8 *)&hw_priv->addresses[0].addr[5]); + } +#endif + if (hw_priv->addresses[0].addr[3] == 0 && + hw_priv->addresses[0].addr[4] == 0 && + hw_priv->addresses[0].addr[5] == 0) + get_random_bytes(&hw_priv->addresses[0].addr[3], 3); +} + +static void bes2600_derive_mac(struct bes2600_common *hw_priv) +{ + memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN); + hw_priv->addresses[1].addr[5] = + hw_priv->addresses[0].addr[5] + 1; + +#ifdef P2P_MULTIVIF + memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr, + ETH_ALEN); + hw_priv->addresses[2].addr[4] ^= 0x80; +#endif +} + +struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) +{ + int i; + struct ieee80211_hw *hw; + struct bes2600_common *hw_priv; + struct ieee80211_supported_band *sband; + int band; + + hw = ieee80211_alloc_hw(hw_priv_data_len, &bes2600_ops); + if (!hw) + return NULL; + + hw_priv = hw->priv; + /* TODO:COMBO this debug message can be removed */ + bes2600_err(BES2600_DBG_INIT, "Allocated hw_priv @ %p\n", hw_priv); + hw_priv->if_id_slot = 0; + hw_priv->roc_if_id = -1; + hw_priv->scan_switch_if_id = -1; + atomic_set(&hw_priv->num_vifs, 0); + atomic_set(&hw_priv->netdevice_start, 0); + + bes2600_get_base_mac(hw_priv); + bes2600_derive_mac(hw_priv); + + hw_priv->hw = hw; + hw_priv->rates = bes2600_rates; /* TODO: fetch from FW */ + hw_priv->mcs_rates = bes2600_n_rates; +#ifdef ROAM_OFFLOAD + hw_priv->auto_scanning = 0; + hw_priv->frame_rcvd = 0; + hw_priv->num_scanchannels = 0; + hw_priv->num_2g_channels = 0; + hw_priv->num_5g_channels = 0; +#endif /*ROAM_OFFLOAD*/ +#ifdef AP_AGGREGATE_FW_FIX + /* Enable block ACK for 4 TID (BE,VI,VI,VO). */ + /*due to HW limitations*/ + hw_priv->ba_tid_mask = 0xB1; +#else + /* Enable block ACK for every TID but voice. */ + hw_priv->ba_tid_mask = 0xFF;//0x3F; +#endif + + /* Init tx retry limit */ +#ifdef BES2600_TX_RX_OPT + hw_priv->long_frame_max_tx_count = 31; + hw_priv->short_frame_max_tx_count = 31; +#else + hw_priv->long_frame_max_tx_count = 7; + hw_priv->short_frame_max_tx_count = 15; +#endif + hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count; + + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, MFP_CAPABLE); + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); +#ifdef P2P_MULTIVIF + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE); +#endif + + /* Support only for limited wowlan functionalities */ +#ifdef CONFIG_PM + hw->wiphy->wowlan = &bes2600_wowlan_support; +#endif + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + +#if defined(CONFIG_BES2600_DISABLE_BEACON_HINTS) + hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; +#endif + hw->wiphy->n_addresses = CW12XX_MAX_VIFS; + hw->wiphy->addresses = hw_priv->addresses; + hw->wiphy->max_remain_on_channel_duration = 500; + hw->wiphy->reg_notifier = bes2600_reg_notifier; + //hw->channel_change_time = 500; /* TODO: find actual value */ + /* hw_priv->beacon_req_id = cpu_to_le32(0); */ + hw->queues = 4; + hw_priv->noise = -94; + + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV and MIC */; + + hw->sta_data_size = sizeof(struct bes2600_sta_priv); + hw->vif_data_size = sizeof(struct bes2600_vif); + + hw->wiphy->bands[NL80211_BAND_2GHZ] = &bes2600_band_2ghz; +#ifdef CONFIG_BES2600_5GHZ_SUPPORT + hw->wiphy->bands[NL80211_BAND_5GHZ] = &bes2600_band_5ghz; +#endif /* CONFIG_BES2600_5GHZ_SUPPORT */ + + /* Channel params have to be cleared before registering wiphy again */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + for (band = 0; band < NUM_NL80211_BANDS; band++) { +#else + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { +#endif + sband = hw->wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].flags = 0; + sband->channels[i].max_antenna_gain = 0; + sband->channels[i].max_power = 30; + } + } + + hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + + hw->wiphy->iface_combinations = bes2600_if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(bes2600_if_comb); + +#ifdef CONFIG_BES2600_WAPI_SUPPORT + hw_priv->last_ins_wapi_usk_id = -1; + hw_priv->last_del_wapi_usk_id = -1; + bes2600_init_wapi_cipher(hw); +#endif + + SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr); + + spin_lock_init(&hw_priv->vif_list_lock); + sema_init(&hw_priv->wsm_cmd_sema, 1); + sema_init(&hw_priv->conf_lock, 1); + sema_init(&hw_priv->wsm_oper_lock, 1); +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_init(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + hw_priv->workqueue = create_singlethread_workqueue("bes2600_wq"); + sema_init(&hw_priv->scan.lock, 1); + INIT_WORK(&hw_priv->scan.work, bes2600_scan_work); +#ifdef ROAM_OFFLOAD + INIT_WORK(&hw_priv->scan.swork, bes2600_sched_scan_work); +#endif /*ROAM_OFFLOAD*/ + INIT_DELAYED_WORK(&hw_priv->scan.probe_work, bes2600_probe_work); + INIT_DELAYED_WORK(&hw_priv->scan.timeout, bes2600_scan_timeout); +#ifdef CONFIG_BES2600_TESTMODE + INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout, + bes2600_advance_scan_timeout); +#endif + INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, bes2600_rem_chan_timeout); + hw_priv->rtsvalue = 0; + spin_lock_init(&hw_priv->rtsvalue_lock); + INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work); + INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); + spin_lock_init(&hw_priv->event_queue_lock); + INIT_LIST_HEAD(&hw_priv->event_queue); + INIT_WORK(&hw_priv->event_handler, bes2600_event_handler); + INIT_WORK(&hw_priv->ba_work, bes2600_ba_work); + spin_lock_init(&hw_priv->ba_lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0); +#else + setup_timer(&hw_priv->ba_timer, bes2600_ba_timer, (unsigned long)hw_priv); +#endif + + if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats, + WLAN_LINK_ID_MAX, + bes2600_skb_dtor, + hw_priv))) { + ieee80211_free_hw(hw); + return NULL; + } + + for (i = 0; i < 4; ++i) { + if (unlikely(bes2600_queue_init(&hw_priv->tx_queue[i], + &hw_priv->tx_queue_stats, i, CW12XX_MAX_QUEUE_SZ, + bes2600_ttl[i]))) { + for (; i > 0; i--) + bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]); + bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&hw_priv->channel_switch_done); + init_waitqueue_head(&hw_priv->wsm_cmd_wq); + init_waitqueue_head(&hw_priv->wsm_startup_done); + init_waitqueue_head(&hw_priv->offchannel_wq); + hw_priv->offchannel_done = 0; + wsm_buf_init(&hw_priv->wsm_cmd_buf); + spin_lock_init(&hw_priv->wsm_cmd.lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + timer_setup(&hw_priv->mcu_mon_timer, bes2600_bh_mcu_active_monitor, 0); + timer_setup(&hw_priv->lmac_mon_timer, bes2600_bh_lmac_active_monitor, 0); +#else + setup_timer(&hw_priv->mcu_mon_timer, bes2600_bh_mcu_active_monitor,(unsigned long)hw_priv); + setup_timer(&hw_priv->lmac_mon_timer, bes2600_bh_lmac_active_monitor,(unsigned long)hw_priv); +#endif + + bes2600_tx_loop_init(hw_priv); + +#ifdef CONFIG_BES2600_TESTMODE + hw_priv->test_frame.data = NULL; + hw_priv->test_frame.len = 0; +#endif /* CONFIG_BES2600_TESTMODE */ + +#ifdef CONFIG_BES2600_VENDOR_CMD + bes2600_set_vendor_command(hw->wiphy); +#endif + +#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT) + hw_priv->wsm_dump_max_size = 20; +#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */ + + for (i = 0; i < CW12XX_MAX_VIFS; i++) + hw_priv->hw_bufs_used_vif[i] = 0; + +#ifdef MCAST_FWDING + for (i = 0; i < WSM_MAX_BUF; i++) + wsm_init_release_buffer_request(hw_priv, i); + hw_priv->buf_released = 0; +#endif + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE; + return hw; +} +EXPORT_SYMBOL_GPL(bes2600_init_common); + + +int bes2600_register_common(struct ieee80211_hw *dev) +{ + struct bes2600_common *hw_priv = dev->priv; + int err; + + err = ieee80211_register_hw(dev); + if (err) { + bes2600_err(BES2600_DBG_INIT, "Cannot register device (%d).\n", + err); + return err; + } + +#ifdef CONFIG_BES2600_LEDS + err = bes2600_init_leds(priv); + if (err) { + bes2600_pm_deinit(&hw_priv->pm_state); + ieee80211_unregister_hw(dev); + return err; + } +#endif /* CONFIG_BES2600_LEDS */ + + bes2600_debug_init_common(hw_priv); + + bes2600_info(BES2600_DBG_INIT, "is registered as '%s'\n", + wiphy_name(dev->wiphy)); + return 0; +} +EXPORT_SYMBOL_GPL(bes2600_register_common); + +void bes2600_free_common(struct ieee80211_hw *dev) +{ + /* struct bes2600_common *hw_priv = dev->priv; */ +#ifdef CONFIG_BES2600_TESTMODE + struct bes2600_common *hw_priv = dev->priv; + kfree(hw_priv->test_frame.data); +#endif /* CONFIG_BES2600_TESTMODE */ + +#ifdef CONFIG_BES2600_VENDOR_CMD + bes2600_vendor_command_detach(dev->wiphy); +#endif + + /* unsigned int i; */ + + ieee80211_free_hw(dev); +} +EXPORT_SYMBOL_GPL(bes2600_free_common); + +void bes2600_unregister_common(struct ieee80211_hw *dev) +{ + struct bes2600_common *hw_priv = dev->priv; + int i; + + ieee80211_unregister_hw(dev); + + del_timer_sync(&hw_priv->ba_timer); + + hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv); + bes2600_unregister_bh(hw_priv); + + bes2600_debug_release_common(hw_priv); + +#ifdef CONFIG_BES2600_LEDS + bes2600_unregister_leds(hw_priv); +#endif /* CONFIG_BES2600_LEDS */ + + wsm_buf_deinit(&hw_priv->wsm_cmd_buf); + destroy_workqueue(hw_priv->workqueue); + hw_priv->workqueue = NULL; + if (hw_priv->skb_cache) { + dev_kfree_skb(hw_priv->skb_cache); + hw_priv->skb_cache = NULL; + } + if (hw_priv->sdd) { +#ifndef CONFIG_BES2600_STATIC_SDD + release_firmware(hw_priv->sdd); +#endif + hw_priv->sdd = NULL; + } + for (i = 0; i < 4; ++i) + bes2600_queue_deinit(&hw_priv->tx_queue[i]); + bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats); + for (i = 0; i < CW12XX_MAX_VIFS; i++) { + kfree(hw_priv->vif_list[i]); + hw_priv->vif_list[i] = NULL; + } + + bes2600_pwr_exit(hw_priv); +} +EXPORT_SYMBOL_GPL(bes2600_unregister_common); + +#if 0 +static void cw12xx_set_ifce_comb(struct bes2600_common *hw_priv, + struct ieee80211_hw *hw) +{ +#ifdef P2P_MULTIVIF + hw_priv->if_limits1[0].max = 2; +#else + hw_priv->if_limits1[0].max = 1; +#endif + + hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION); + hw_priv->if_limits1[1].max = 1; + hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP); + +#ifdef P2P_MULTIVIF + hw_priv->if_limits2[0].max = 3; +#else + hw_priv->if_limits2[0].max = 2; +#endif + hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION); + +#ifdef P2P_MULTIVIF + hw_priv->if_limits3[0].max = 2; +#else + hw_priv->if_limits3[0].max = 1; +#endif + + hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION); + hw_priv->if_limits3[1].max = 1; + hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + /* TODO:COMBO: mac80211 doesn't yet support more than 1 + * different channel */ + hw_priv->if_combs[0].num_different_channels = 1; +#ifdef P2P_MULTIVIF + hw_priv->if_combs[0].max_interfaces = 3; +#else + hw_priv->if_combs[0].max_interfaces = 2; +#endif + hw_priv->if_combs[0].limits = hw_priv->if_limits1; + hw_priv->if_combs[0].n_limits = 2; + + hw_priv->if_combs[1].num_different_channels = 1; + +#ifdef P2P_MULTIVIF + hw_priv->if_combs[1].max_interfaces = 3; +#else + hw_priv->if_combs[1].max_interfaces = 2; +#endif + hw_priv->if_combs[1].limits = hw_priv->if_limits2; + hw_priv->if_combs[1].n_limits = 1; + + hw_priv->if_combs[2].num_different_channels = 1; +#ifdef P2P_MULTIVIF + hw_priv->if_combs[2].max_interfaces = 3; +#else + hw_priv->if_combs[2].max_interfaces = 2; +#endif + hw_priv->if_combs[2].limits = hw_priv->if_limits3; + hw_priv->if_combs[2].n_limits = 2; + + hw->wiphy->iface_combinations = &hw_priv->if_combs[0]; + hw->wiphy->n_iface_combinations = 3; + +} +#endif +static int bes2600_sbus_comm_init(struct bes2600_common *hw_priv) +{ + int ret = 0; + +#if defined(FW_DOWNLOAD_BY_USB) + if (hw_priv->sbus_ops->reset) + hw_priv->sbus_ops->reset(hw_priv->sbus_priv); +#endif + +#ifndef CONFIG_BES2600_WLAN_SPI + if (hw_priv->sbus_ops->init) + hw_priv->sbus_ops->init(hw_priv->sbus_priv, hw_priv); +#endif + + /* Register Interrupt Handler */ + hw_priv->sbus_ops->irq_subscribe(hw_priv->sbus_priv, + (sbus_irq_handler)bes2600_irq_handler, hw_priv); + hw_priv->hw_type = HIF_8601_SILICON; + hw_priv->hw_revision = BES2600_HW_REV_CUT10; + + return ret; +} + +int bes2600_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct bes2600_common **pself) +{ + int err = -ENOMEM; + //u16 ctrl_reg; + struct ieee80211_hw *dev; + struct bes2600_common *hw_priv; + +#if defined(CONFIG_BES2600_WLAN_USB) + int if_id; + +#ifdef CUSTOM_FEATURE/* To control ps mode */ + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + char buffer[2]; + savedpsm = mode.power_mode; + if(access_file(PATH_WIFI_PSM_INFO,buffer,2,1) > 0) { + if(buffer[0] == 0x30) { + mode.power_mode = wsm_power_mode_active; + } + else + { + if(savedpsm) + mode.power_mode = savedpsm; + else /* Set default */ + mode.power_mode = wsm_power_mode_quiescent; + } + bes2600_info(BES2600_DBG_INIT, "BES2600 : PSM changed to %d\n",mode.power_mode); + } + else { + bes2600_info(BES2600_DBG_INIT, "BES2600 : Using default PSM %d\n",mode.power_mode); + } +#endif +#endif + + dev = bes2600_init_common(sizeof(struct bes2600_common)); + if (!dev) + goto err; + + hw_priv = dev->priv; + hw_priv->sbus_ops = sbus_ops; + hw_priv->sbus_priv = sbus; + hw_priv->pdev = pdev; + SET_IEEE80211_DEV(hw_priv->hw, pdev); + + /* WSM callbacks. */ + hw_priv->wsm_cbc.scan_complete = bes2600_scan_complete_cb; + hw_priv->wsm_cbc.tx_confirm = bes2600_tx_confirm_cb; + hw_priv->wsm_cbc.rx = bes2600_rx_cb; + hw_priv->wsm_cbc.suspend_resume = bes2600_suspend_resume; + /* hw_priv->wsm_cbc.set_pm_complete = bes2600_set_pm_complete_cb; */ + hw_priv->wsm_cbc.channel_switch = bes2600_channel_switch_cb; + + bes2600_pwr_init(hw_priv); + + err = bes2600_register_bh(hw_priv); + if (err) + goto err1; + + err = bes2600_sbus_comm_init(hw_priv); + if (err) + goto err2; + + if (bes2600_chrdev_get_fw_type() == BES2600_FW_TYPE_BT) { + } else if (bes2600_chrdev_get_fw_type() == BES2600_FW_TYPE_WIFI_NO_SIGNAL) { +#ifdef CONFIG_BES2600_WLAN_BES + *pself = dev->priv; +#endif +#if defined(CONFIG_BES2600_WLAN_SDIO) || defined(CONFIG_BES2600_WLAN_SPI) + if (bes2600_wifi_start(hw_priv)) + goto err3; +#else + mdelay(2000); + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->wsm_caps.firmwareReady, 10*HZ) <= 0) { + bes2600_info(BES2600_DBG_INIT, "startup timeout!!!\n"); + err = -ENODEV; + goto err3; + } +#endif +#if defined(CONFIG_BES2600_WLAN_USB) + } else { + mdelay(2000); + + /* + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + WARN_ON(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv, + SDIO_BLOCK_SIZE)); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + + cw12xx_set_ifce_comb(hw_priv, dev); + + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + WARN_ON(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv, + SDIO_BLOCK_SIZE)); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + */ + + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->wsm_caps.firmwareReady, 10*HZ) <= 0) { + + /* TODO: Needs to find how to reset device */ + /* in QUEUE mode properly. */ + bes2600_info(BES2600_DBG_INIT, "startup timeout!!!\n"); + err = -ENODEV; + goto err3; + } + /* + WARN_ON(bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT)); + + if (bes2600_reg_read_16(hw_priv,ST90TDS_CONTROL_REG_ID, &ctrl_reg)) + WARN_ON(bes2600_reg_read_16(hw_priv,ST90TDS_CONTROL_REG_ID, + &ctrl_reg)); + + WARN_ON(!(ctrl_reg & ST90TDS_CONT_RDY_BIT)); + */ + for (if_id = 0; if_id < 2; if_id++) { + /* Set low-power mode. */ + /* Enable multi-TX confirmation */ + WARN_ON(wsm_use_multi_tx_conf(hw_priv, true, if_id)); + } +#endif + } + + err = bes2600_register_common(dev); + if (err) { + goto err3; + } + + *pself = dev->priv; + return err; + +err3: + hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv); + if (sbus_ops->reset) + sbus_ops->reset(sbus); +err2: + bes2600_unregister_bh(hw_priv); +err1: + bes2600_free_common(dev); +err: + return err; +} + +void bes2600_core_release(struct bes2600_common *self) +{ + bes2600_unregister_common(self->hw); + bes2600_free_common(self->hw); + return; +} + +#if defined(CUSTOM_FEATURE) ||(GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */ +int access_file(char *path, char *buffer, int size, int isRead) +{ + int ret=0; + struct file *fp; + mm_segment_t old_fs = get_fs(); + + if(isRead) + fp = filp_open(path,O_RDONLY,S_IRUSR); + else + fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR); + + if (IS_ERR(fp)) { + bes2600_err(BES2600_DBG_INIT, "BES2600 : can't open %s\n",path); + return -1; + } + + if(isRead) + { + fp->f_pos = 0; + set_fs(KERNEL_DS); + ret = vfs_read(fp,buffer,size,&fp->f_pos); + set_fs(old_fs); + } + else + { + fp->f_pos = 0; + set_fs(KERNEL_DS); + ret = vfs_write(fp,buffer,size,&fp->f_pos); + set_fs(old_fs); + } + filp_close(fp,NULL); + + bes2600_info(BES2600_DBG_INIT, "BES2600 : access_file return code(%d)\n",ret); + return ret; +} +#endif + +#ifdef CONFIG_BES2600_WLAN_BES +int bes2600_wifi_start(struct bes2600_common *hw_priv) +{ + int ret, if_id; +#ifndef CONFIG_BES2600_WLAN_USB + if(hw_priv->sbus_ops->gpio_wake) { + hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON); + } + + if (hw_priv->sbus_ops->sbus_active && + WARN_ON((ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_WIFI)))) + goto err; +#endif + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->wsm_caps.firmwareReady, 10*HZ) <= 0) { + + /* TODO: Needs to find how to reset device */ + /* in QUEUE mode properly. */ + bes2600_info(BES2600_DBG_INIT, "startup timeout!!!\n"); + ret = -ENODEV; + goto err; + } + + if (bes2600_chrdev_is_signal_mode()) { + for (if_id = 0; if_id < 2; if_id++) { + /* Enable multi-TX confirmation */ + if (WARN_ON((ret = wsm_use_multi_tx_conf(hw_priv, true, if_id)))) { + goto err; + } + } + } + bes2600_pwr_start(hw_priv); + +err: + if(hw_priv->sbus_ops->gpio_sleep) { + hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON); + } + + return ret; +} + +int bes2600_wifi_stop(struct bes2600_common *hw_priv) +{ + int ret; + unsigned long status = 0; + + status = wait_event_timeout(hw_priv->bh_evt_wq, (!hw_priv->hw_bufs_used), 3 * HZ); + bes2600_err_with_cond((!status), BES2600_DBG_INIT, "communication exception!"); + + if(hw_priv->sbus_ops->gpio_wake) { + hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF); + } + + bes2600_pwr_stop(hw_priv); + + if (hw_priv->sbus_ops->sbus_deactive && + WARN_ON(ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_WIFI))) { + goto err; + } + + if(hw_priv->sbus_ops->gpio_sleep) { + hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF); + } + + memset(&hw_priv->wsm_caps, 0, sizeof(hw_priv->wsm_caps)); + hw_priv->wsm_rx_seq[0] = 0; + hw_priv->wsm_rx_seq[1] = 0; + hw_priv->wsm_tx_seq[0] = 0; + hw_priv->wsm_tx_seq[1] = 0; + hw_priv->wsm_tx_pending[0] = 0; + hw_priv->wsm_tx_pending[1] = 0; + del_timer_sync(&hw_priv->mcu_mon_timer); + del_timer_sync(&hw_priv->lmac_mon_timer); +#ifdef CONFIG_BES2600_STATIC_SDD + hw_priv->sdd = NULL; +#else + #error "TO BE CONTINUED: release SDD file" +#endif + return ret; + +err: + if(hw_priv->sbus_ops->gpio_sleep) { + hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF); + } + + return ret; +} +#endif diff --git a/drivers/staging/bes2600/pm.c b/drivers/staging/bes2600/pm.c new file mode 100644 index 000000000000..fbd6a8867332 --- /dev/null +++ b/drivers/staging/bes2600/pm.c @@ -0,0 +1,474 @@ +/* + * Mac80211 power management API for BES2600 drivers + * + * Copyright (c) 2011, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "bes2600.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "sbus.h" +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" + +#define BES2600_BEACON_SKIPPING_MULTIPLIER 3 + +struct bes2600_udp_port_filter { + struct wsm_udp_port_filter_hdr hdr; + struct wsm_udp_port_filter dhcp; + struct wsm_udp_port_filter upnp; + struct wsm_udp_port_filter mdns; +} __packed; + +struct bes2600_ether_type_filter { + struct wsm_ether_type_filter_hdr hdr; + struct wsm_ether_type_filter ip; + struct wsm_ether_type_filter pae; + struct wsm_ether_type_filter wapi; +} __packed; + +static struct bes2600_udp_port_filter bes2600_udp_port_filter_on = { + .hdr.nrFilters = 3, + .dhcp = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(67), + }, + .upnp = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(1900), + }, + .mdns = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(5353), + }, + /* Please add other known ports to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; + +static struct wsm_udp_port_filter_hdr bes2600_udp_port_filter_off = { + .nrFilters = 0, +}; + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + +static struct bes2600_ether_type_filter bes2600_ether_type_filter_on = { + .hdr.nrFilters = 3, + .ip = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .etherType = __cpu_to_le16(ETH_P_IPV6), + }, + .pae = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_PAE), + }, + .wapi = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_WAPI), + }, + /* Please add other known ether types to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; + +static struct wsm_ether_type_filter_hdr bes2600_ether_type_filter_off = { + .nrFilters = 0, +}; + +static int __bes2600_wow_suspend(struct bes2600_vif *priv, + struct cfg80211_wowlan *wowlan); +static int __bes2600_wow_resume(struct bes2600_vif *priv); + + +/* private */ +struct bes2600_suspend_state { + unsigned long bss_loss_tmo; + unsigned long connection_loss_tmo; + unsigned long join_tmo; + unsigned long direct_probe; + unsigned long link_id_gc; +}; + +static long bes2600_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int bes2600_resume_work(struct bes2600_common *hw_priv, + struct delayed_work *work, + unsigned long tmo) +{ + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(hw_priv->workqueue, work, tmo); +} + +int bes2600_can_suspend(struct bes2600_common *priv) +{ + if (atomic_read(&priv->bh_rx)) { + wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); + return 0; + } + return 1; +} +EXPORT_SYMBOL_GPL(bes2600_can_suspend); + +int bes2600_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv; + int i, ret = 0; + unsigned long begin, end, diff; + + bes2600_info(BES2600_DBG_PM, "bes2600_wow_suspend enter\n"); + + WARN_ON(!atomic_read(&hw_priv->num_vifs)); + + /* reset wakeup reason to default */ + bes2600_chrdev_wifi_update_wakeup_reason(0); + +#ifdef ROAM_OFFLOAD + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + if((priv->vif->type == NL80211_IFTYPE_STATION) + && (priv->join_status == BES2600_JOIN_STATUS_STA)) { + down(&hw_priv->scan.lock); + hw_priv->scan.if_id = priv->if_id; + bes2600_sched_scan_work(&hw_priv->scan.swork); + } + } +#endif /*ROAM_OFFLOAD*/ + + /* Do not suspend when datapath is not idle */ + if (hw_priv->tx_queue_stats.num_queued[0] + + hw_priv->tx_queue_stats.num_queued[1]) + return -EBUSY; + + + /* Make sure there is no configuration requests in progress. */ + if (down_trylock(&hw_priv->conf_lock)) + return -EBUSY; + + /* Do not suspend when scanning or ROC*/ + if (down_trylock(&hw_priv->scan.lock)) + goto revert1; + + /* Do not suspend when probe is doing */ + if (delayed_work_pending(&hw_priv->scan.probe_work)) + goto revert2; + + /* record suspend start time */ + begin = jiffies; + + /* wait uitil bes2600 finish current pending operation */ + if (!bes2600_pwr_device_is_idle(hw_priv)) { + /* clear power busy event */ + bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_TX, 10); + + /* wait device enter lp mode */ + if (wait_event_timeout(hw_priv->bes_power.dev_lp_wq, + bes2600_pwr_device_is_idle(hw_priv), HZ * 5) <= 0) { + bes2600_err(BES2600_DBG_PM, "wait device idle timeout\n"); + goto revert2; + } + } + + /* Lock TX. */ + wsm_lock_tx_async(hw_priv); + + /* mark suspend start to avoid device to exit ps mode when setting device */ + bes2600_pwr_suspend_start(hw_priv); + + /* set filters and offload based on interface */ + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + + ret = __bes2600_wow_suspend(priv, + wowlan); + if (ret) { + for (; i >= 0; i--) { + if (!hw_priv->vif_list[i]) + continue; + priv = (struct bes2600_vif *) + hw_priv->vif_list[i]->drv_priv; + __bes2600_wow_resume(priv); + } + goto revert3; + } + } + + /* mark suspend end */ + bes2600_pwr_suspend_end(hw_priv); + + /* Stop serving thread */ + if (bes2600_bh_suspend(hw_priv)) { + bes2600_err(BES2600_DBG_PM, "%s: bes2600_bh_suspend failed\n", + __func__); + bes2600_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&hw_priv->bh_rx)) { + bes2600_info(BES2600_DBG_PM, "%s: incoming event present - resume\n", + __func__); + bes2600_wow_resume(hw); + return -EAGAIN; + } + + /* calculate the time consumed by bes2600 suspend flow */ + end = jiffies; + diff = end - begin; + bes2600_info(BES2600_DBG_PM, "%s consume %d ms\n", __func__, jiffies_to_msecs(diff)); + + return 0; + +revert3: + bes2600_pwr_suspend_end(hw_priv); + wsm_unlock_tx(hw_priv); +revert2: + up(&hw_priv->scan.lock); +revert1: + up(&hw_priv->conf_lock); + return -EBUSY; +} + +static void bes2600_set_ehter_and_udp_filter(struct bes2600_common *hw_priv, + struct wsm_ether_type_filter_hdr *ether_type, struct wsm_udp_port_filter_hdr *udp_type, + int if_id) +{ + /* Set UDP filter */ + wsm_set_udp_port_filter(hw_priv, udp_type, if_id); + + /* Set ethernet frame type filter */ + wsm_set_ether_type_filter(hw_priv, ether_type, if_id); +} + +static int __bes2600_wow_suspend(struct bes2600_vif *priv, + struct cfg80211_wowlan *wowlan) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif; + struct bes2600_suspend_state *state; + int ret; + +#ifdef MCAST_FWDING + struct wsm_forwarding_offload fwdoffload = { + .fwenable = 0x1, + .flags = 0x1, + }; +#endif + + /* Do not suspend when join work is scheduled */ + if (work_pending(&priv->join_work)) + goto revert1; + + bes2600_set_ehter_and_udp_filter(hw_priv, &bes2600_ether_type_filter_on.hdr, + &bes2600_udp_port_filter_on.hdr, priv->if_id); + + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(wsm_set_keepalive_filter(priv, true)); + + /* Set Multicast Address Filter */ + if (priv->multicast_filter.numOfAddresses) { + priv->multicast_filter.enable = __cpu_to_le32(2); + wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } + +#ifdef MCAST_FWDING + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(wsm_set_forwarding_offlad(hw_priv, + &fwdoffload,priv->if_id)); +#endif + + /* Allocate state */ + state = kzalloc(sizeof(struct bes2600_suspend_state), GFP_KERNEL); + if (!state) + goto revert2; + + /* Store delayed work states. */ + state->bss_loss_tmo = + bes2600_suspend_work(&priv->bss_loss_work); + state->connection_loss_tmo = + bes2600_suspend_work(&priv->connection_loss_work); + state->join_tmo = + bes2600_suspend_work(&priv->join_timeout); + state->link_id_gc = + bes2600_suspend_work(&priv->link_id_gc_work); + + ret = timer_pending(&priv->mcast_timeout); + if (ret) + goto revert3; + + /* Store suspend state */ + pm_state_vif->suspend_state = state; + + return 0; + +revert3: + bes2600_resume_work(hw_priv, &priv->bss_loss_work, + state->bss_loss_tmo); + bes2600_resume_work(hw_priv, &priv->connection_loss_work, + state->connection_loss_tmo); + bes2600_resume_work(hw_priv, &priv->join_timeout, + state->join_tmo); + bes2600_resume_work(hw_priv, &priv->link_id_gc_work, + state->link_id_gc); + kfree(state); +revert2: + wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off, + priv->if_id); + wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off, + priv->if_id); + + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(wsm_set_keepalive_filter(priv, false)); + + /* Set Multicast Address Filter */ + if (priv->multicast_filter.numOfAddresses) { + priv->multicast_filter.enable = __cpu_to_le32(1); + wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } + + +#ifdef MCAST_FWDING + fwdoffload.flags = 0x0; + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); +#endif +revert1: + up(&hw_priv->conf_lock); + return -EBUSY; +} + +int bes2600_wow_resume(struct ieee80211_hw *hw) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv; + int i, ret = 0; + + bes2600_info(BES2600_DBG_PM, "bes2600_wow_resume enter\n"); + WARN_ON(!atomic_read(&hw_priv->num_vifs)); + + up(&hw_priv->scan.lock); + + /* Resume BH thread */ + WARN_ON(bes2600_bh_resume(hw_priv)); + + /* mark resume start to avoid device to exit ps mode when setting device */ + bes2600_pwr_resume_start(hw_priv); + + /* set filters and offload based on interface */ + bes2600_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + ret = __bes2600_wow_resume(priv); + if (ret) + break; + } + + /* mark resume end */ + bes2600_pwr_resume_end(hw_priv); + + wsm_unlock_tx(hw_priv); + /* Unlock configuration mutex */ + up(&hw_priv->conf_lock); + + return ret; +} + +static int __bes2600_wow_resume(struct bes2600_vif *priv) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif; + struct bes2600_suspend_state *state; + +#ifdef MCAST_FWDING + struct wsm_forwarding_offload fwdoffload = { + .fwenable = 0x1, + .flags = 0x0, + }; +#endif + state = pm_state_vif->suspend_state; + pm_state_vif->suspend_state = NULL; + +#ifdef ROAM_OFFLOAD + if((priv->vif->type == NL80211_IFTYPE_STATION) + && (priv->join_status == BES2600_JOIN_STATUS_STA)) + bes2600_hw_sched_scan_stop(hw_priv); +#endif /*ROAM_OFFLOAD*/ + + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(wsm_set_keepalive_filter(priv, false)); + + /* Set Multicast Address Filter */ + if (priv->multicast_filter.numOfAddresses) { + priv->multicast_filter.enable = __cpu_to_le32(1); + wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } + +#ifdef MCAST_FWDING + if (priv->join_status == BES2600_JOIN_STATUS_AP) + WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); +#endif + + /* Resume delayed work */ + bes2600_resume_work(hw_priv, &priv->bss_loss_work, + state->bss_loss_tmo); + bes2600_resume_work(hw_priv, &priv->connection_loss_work, + state->connection_loss_tmo); + bes2600_resume_work(hw_priv, &priv->join_timeout, + state->join_tmo); + bes2600_resume_work(hw_priv, &priv->link_id_gc_work, + state->link_id_gc); + + /* Remove UDP port filter */ + wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off, + priv->if_id); + + /* Remove ethernet frame type filter */ + wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off, + priv->if_id); + /* Free memory */ + kfree(state); + + return 0; +} diff --git a/drivers/staging/bes2600/pm.h b/drivers/staging/bes2600/pm.h new file mode 100644 index 000000000000..dbf887367877 --- /dev/null +++ b/drivers/staging/bes2600/pm.h @@ -0,0 +1,35 @@ +/* + * Mac80211 power management interface for BES2600 mac80211 drivers + * + * Copyright (c) 2011, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +#ifdef CONFIG_PM + +/* extern */ struct bes2600_common; +/* private */ struct bes2600_suspend_state; + +struct bes2600_pm_state_vif { + struct bes2600_suspend_state *suspend_state; +}; + +int bes2600_can_suspend(struct bes2600_common *priv); +int bes2600_wow_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int bes2600_wow_resume(struct ieee80211_hw *hw); +#else +static inline int bes2600_can_suspend(struct bes2600_common *priv) +{ + return 0; +} +#endif /* CONFIG_PM */ + +#endif diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c new file mode 100644 index 000000000000..de8edb2a3433 --- /dev/null +++ b/drivers/staging/bes2600/queue.c @@ -0,0 +1,931 @@ +/* + * O(1) TX queue with built-in allocator for BES2600 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "bes2600.h" +#include "queue.h" +#include "debug.h" +#ifdef CONFIG_BES2600_TESTMODE +#include +#endif /*CONFIG_BES2600_TESTMODE*/ + +/* private */ struct bes2600_queue_item +{ + struct list_head head; + struct sk_buff *skb; + u32 packetID; + unsigned long queue_timestamp; + unsigned long xmit_timestamp; +#ifdef CONFIG_BES2600_TESTMODE + unsigned long mdelay_timestamp; + unsigned long qdelay_timestamp; +#endif /*CONFIG_BES2600_TESTMODE*/ + struct bes2600_txpriv txpriv; + u8 generation; +}; + +int bes2600_queue_get_skb_and_timestamp(struct bes2600_queue *queue, u32 packetID, + struct sk_buff **skb, struct bes2600_txpriv **txpriv, + unsigned long *timestamp) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct bes2600_queue_item *item; + bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + if (unlikely(item_id >= (u8) queue->capacity)) + return -EINVAL; + item = &queue->pool[item_id]; + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + WARN(1, "queue generation mismatch, %u, expect %u, if_id: %u.\n", + queue_generation, queue->generation, if_id); + ret = -ENOENT; + } else if (unlikely(item_generation != item->generation)) { + WARN(1, "item generation mismatch, %u, expect %u.\n", + item_generation, item->generation); + ret = -ENOENT; + } else if (unlikely(WARN_ON(!item->skb))) { + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + *timestamp = item->xmit_timestamp; + } + spin_unlock_bh(&queue->lock); + return ret; +} + + +static inline void __bes2600_queue_lock(struct bes2600_queue *queue) +{ + struct bes2600_queue_stats *stats = queue->stats; + if (queue->tx_locked_cnt++ == 0) { + bes2600_dbg(BES2600_DBG_TXRX, "[TX] Queue %d is locked.\n", + queue->queue_id); + ieee80211_stop_queue(stats->hw_priv->hw, queue->queue_id); + } +} + +static inline void __bes2600_queue_unlock(struct bes2600_queue *queue) +{ + struct bes2600_queue_stats *stats = queue->stats; + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) { + bes2600_dbg(BES2600_DBG_TXRX, "[TX] Queue %d is unlocked.\n", + queue->queue_id); + ieee80211_wake_queue(stats->hw_priv->hw, queue->queue_id); + } +} + + + +static inline u32 bes2600_queue_make_packet_id(u8 queue_generation, u8 queue_id, + u8 item_generation, u8 item_id, + u8 if_id, u8 link_id) +{ + /*TODO:COMBO: Add interfaceID to the packetID */ + return ((u32)item_id << 0) | + ((u32)item_generation << 8) | + ((u32)queue_id << 16) | + ((u32)if_id << 20) | + ((u32)link_id << 24) | + ((u32)queue_generation << 28); +} + +static void bes2600_queue_post_gc(struct bes2600_queue_stats *stats, + struct list_head *gc_list) +{ + struct bes2600_queue_item *item; + + while (!list_empty(gc_list)) { + item = list_first_entry( + gc_list, struct bes2600_queue_item, head); + list_del(&item->head); + stats->skb_dtor(stats->hw_priv, item->skb, &item->txpriv); + kfree(item); + } +} + +static void bes2600_queue_register_post_gc(struct list_head *gc_list, + struct bes2600_queue_item *item) +{ + struct bes2600_queue_item *gc_item; + gc_item = kmalloc(sizeof(struct bes2600_queue_item), + GFP_ATOMIC); + BUG_ON(!gc_item); + memcpy(gc_item, item, sizeof(struct bes2600_queue_item)); + list_add_tail(&gc_item->head, gc_list); +} + +static void __bes2600_queue_gc(struct bes2600_queue *queue, + struct list_head *head, + bool unlock) +{ + struct bes2600_queue_stats *stats = queue->stats; + struct bes2600_queue_item *item = NULL; + struct bes2600_vif *priv; + int if_id; + bool wakeup_stats = false; + + while (!list_empty(&queue->queue)) { + struct bes2600_txpriv *txpriv; + item = list_first_entry( + &queue->queue, struct bes2600_queue_item, head); + if (jiffies - item->queue_timestamp < queue->ttl) + break; + + txpriv = &item->txpriv; + if_id = txpriv->if_id; + --queue->num_queued; + --queue->num_queued_vif[if_id]; + --queue->link_map_cache[if_id][txpriv->link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued[if_id]; + if (!--stats->link_map_cache[if_id][txpriv->link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + priv = cw12xx_hwpriv_to_vifpriv(stats->hw_priv, if_id); + if (priv) { + bes2600_debug_tx_ttl(priv); + spin_unlock(&priv->vif_lock); + } + bes2600_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + if (queue->overfull) { + if (queue->num_queued <= ((stats->hw_priv->vif0_throttle + + stats->hw_priv->vif1_throttle + 2)/2)) { + queue->overfull = false; + if (unlock) + __bes2600_queue_unlock(queue); + } else if (item) { + unsigned long tmo = item->queue_timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + bes2600_pwr_set_busy_event_with_timeout_async(stats->hw_priv, + BES_PWR_LOCK_ON_QUEUE_GC, jiffies_to_msecs(tmo - jiffies)); + } + } +} +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) +static void bes2600_queue_gc(struct timer_list *t) +{ + LIST_HEAD(list); + struct bes2600_queue *queue = from_timer(queue, t, gc); + + spin_lock_bh(&queue->lock); + __bes2600_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + bes2600_queue_post_gc(queue->stats, &list); +} +#else +static void bes2600_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct bes2600_queue *queue = (struct bes2600_queue *)arg; + + spin_lock_bh(&queue->lock); + __bes2600_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + bes2600_queue_post_gc(queue->stats, &list); +} +#endif + +int bes2600_queue_stats_init(struct bes2600_queue_stats *stats, + size_t map_capacity, + bes2600_queue_skb_dtor_t skb_dtor, + struct bes2600_common *hw_priv) +{ + int i; + + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->hw_priv = hw_priv; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + for (i = 0; i < CW12XX_MAX_VIFS; i++) { + stats->link_map_cache[i] = kzalloc(map_capacity * sizeof(int), + GFP_KERNEL); + if (!stats->link_map_cache[i]) { + for (; i >= 0; i--) + kfree(stats->link_map_cache[i]); + return -ENOMEM; + } + } + + return 0; +} + +int bes2600_queue_init(struct bes2600_queue *queue, + struct bes2600_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl) +{ + int i; + + memset(queue, 0, sizeof(*queue)); + queue->stats = stats; + queue->capacity = capacity; + queue->queue_id = queue_id; + queue->ttl = ttl; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + timer_setup(&queue->gc, bes2600_queue_gc, 0); +#else + setup_timer(&queue->gc, bes2600_queue_gc,(unsigned long)queue); +#endif + queue->pool = kzalloc(sizeof(struct bes2600_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + for (i = 0; i < CW12XX_MAX_VIFS; i++) { + queue->link_map_cache[i] = + kzalloc(stats->map_capacity * sizeof(int), + GFP_KERNEL); + if (!queue->link_map_cache[i]) { + for (; i >= 0; i--) + kfree(queue->link_map_cache[i]); + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +/* TODO:COMBO: Flush only a particular interface specific parts */ +int bes2600_queue_clear(struct bes2600_queue *queue, int if_id) +{ + int i, cnt, iter; + struct bes2600_queue_stats *stats = queue->stats; + LIST_HEAD(gc_list); + + cnt = 0; + spin_lock_bh(&queue->lock); + queue->generation++; + queue->generation &= 0xf; + list_splice_tail_init(&queue->queue, &queue->pending); + while (!list_empty(&queue->pending)) { + struct bes2600_queue_item *item = list_first_entry( + &queue->pending, struct bes2600_queue_item, head); + WARN_ON(!item->skb); + if (CW12XX_ALL_IFS == if_id || item->txpriv.if_id == if_id) { + bes2600_queue_register_post_gc(&gc_list, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + cnt++; + } + } + queue->num_queued -= cnt; + queue->num_pending -= cnt; + if (CW12XX_ALL_IFS != if_id) { + queue->num_queued_vif[if_id] = 0; + queue->num_pending_vif[if_id] = 0; + } else { + for (iter = 0; iter < CW12XX_MAX_VIFS; iter++) { + queue->num_queued_vif[iter] = 0; + queue->num_pending_vif[iter] = 0; + } + } + spin_lock_bh(&stats->lock); + if (CW12XX_ALL_IFS != if_id) { + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued[if_id] -= + queue->link_map_cache[if_id][i]; + stats->link_map_cache[if_id][i] -= + queue->link_map_cache[if_id][i]; + queue->link_map_cache[if_id][i] = 0; + } + } else { + for (iter = 0; iter < CW12XX_MAX_VIFS; iter++) { + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued[iter] -= + queue->link_map_cache[iter][i]; + stats->link_map_cache[iter][i] -= + queue->link_map_cache[iter][i]; + queue->link_map_cache[iter][i] = 0; + } + } + } + spin_unlock_bh(&stats->lock); + if (unlikely(queue->overfull)) { + queue->overfull = false; + __bes2600_queue_unlock(queue); + } + spin_unlock_bh(&queue->lock); + wake_up(&stats->wait_link_id_empty); + bes2600_queue_post_gc(stats, &gc_list); + return 0; +} + +void bes2600_queue_stats_deinit(struct bes2600_queue_stats *stats) +{ + int i; + + for (i = 0; i < CW12XX_MAX_VIFS ; i++) { + kfree(stats->link_map_cache[i]); + stats->link_map_cache[i] = NULL; + } +} + +void bes2600_queue_deinit(struct bes2600_queue *queue) +{ + int i; + + bes2600_queue_clear(queue, CW12XX_ALL_IFS); + del_timer_sync(&queue->gc); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + for (i = 0; i < CW12XX_MAX_VIFS; i++) { + kfree(queue->link_map_cache[i]); + queue->link_map_cache[i] = NULL; + } + queue->pool = NULL; + queue->capacity = 0; +} + +size_t bes2600_queue_get_num_queued(struct bes2600_vif *priv, + struct bes2600_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + size_t map_capacity = queue->stats->map_capacity; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->lock); + if (likely(link_id_map == (u32) -1)) { + ret = queue->num_queued_vif[priv->if_id] - + queue->num_pending_vif[priv->if_id]; + } else { + ret = 0; + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) + ret += + queue->link_map_cache[priv->if_id][i]; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int bes2600_queue_put(struct bes2600_queue *queue, + struct sk_buff *skb, + struct bes2600_txpriv *txpriv) +{ + int ret = 0; +#ifdef CONFIG_BES2600_TESTMODE + struct timeval tmval; +#endif /*CONFIG_BES2600_TESTMODE*/ + + LIST_HEAD(gc_list); + struct bes2600_queue_stats *stats = queue->stats; + /* TODO:COMBO: Add interface ID info to queue item */ + + if (txpriv->link_id >= queue->stats->map_capacity) + return -EINVAL; + + spin_lock_bh(&queue->lock); + if (!WARN_ON(list_empty(&queue->free_pool))) { + struct bes2600_queue_item *item = list_first_entry( + &queue->free_pool, struct bes2600_queue_item, head); + BUG_ON(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->txpriv = *txpriv; + item->generation = 0; + item->packetID = bes2600_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool, + txpriv->if_id, txpriv->raw_link_id); + item->queue_timestamp = jiffies; +#ifdef BES2600_HOST_TIMESTAMP_DEBUG + if (skb_tailroom(skb) >= 4) { + u32 *extra_data; + extra_data = (u32 *)skb_tail_pointer(skb); + *extra_data = (u32)jiffies_to_msecs(item->queue_timestamp); + } +#endif +#ifdef CONFIG_BES2600_TESTMODE + do_gettimeofday(&tmval); + item->qdelay_timestamp = tmval.tv_usec; +#endif /*CONFIG_BES2600_TESTMODE*/ + + ++queue->num_queued; + ++queue->num_queued_vif[txpriv->if_id]; + ++queue->link_map_cache[txpriv->if_id][txpriv->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued[txpriv->if_id]; + ++stats->link_map_cache[txpriv->if_id][txpriv->link_id]; + spin_unlock_bh(&stats->lock); + + /* + * TX may happen in parallel sometimes. + * Leave extra queue slots so we don't overflow. + */ + if (queue->overfull == false && + queue->num_queued >= + ((stats->hw_priv->vif0_throttle + + stats->hw_priv->vif1_throttle + 2) + - (num_present_cpus() - 1))) { + queue->overfull = true; + __bes2600_queue_lock(queue); + mod_timer(&queue->gc, jiffies); + } + } else { + ret = -ENOENT; + } +#if 0 + bes2600_dbg(BES2600_DBG_TXRX, "queue_put queue %d, %d, %d\n", + queue->num_queued, + queue->link_map_cache[txpriv->if_id][txpriv->link_id], + queue->num_pending); + bes2600_dbg(BES2600_DBG_TXRX, "queue_put stats %d, %d\n", stats->num_queued, + stats->link_map_cache[txpriv->if_id][txpriv->link_id]); +#endif + spin_unlock_bh(&queue->lock); + return ret; +} + +int bes2600_queue_get(struct bes2600_queue *queue, + int if_id, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + struct bes2600_txpriv **txpriv) +{ + int ret = -ENOENT; + struct bes2600_queue_item *item = NULL; + struct bes2600_queue_stats *stats = queue->stats; + bool wakeup_stats = false; +#ifdef CONFIG_BES2600_TESTMODE + struct timeval tmval; +#endif /*CONFIG_BES2600_TESTMODE*/ + + spin_lock_bh(&queue->lock); + list_for_each_entry(item, &queue->queue, head) { + if ((item->txpriv.if_id == if_id) && + (link_id_map & BIT(item->txpriv.link_id))) { + ret = 0; + break; + } + } + + if (!ret) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + *txpriv = &item->txpriv; + (*tx)->packetID = __cpu_to_le32(item->packetID); + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + ++queue->num_pending_vif[item->txpriv.if_id]; + --queue->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]; + item->xmit_timestamp = jiffies; +#ifdef CONFIG_BES2600_TESTMODE + do_gettimeofday(&tmval); + item->mdelay_timestamp = tmval.tv_usec; +#endif /*CONFIG_BES2600_TESTMODE*/ + + spin_lock_bh(&stats->lock); + --stats->num_queued[item->txpriv.if_id]; + if (!--stats->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]) + wakeup_stats = true; + + spin_unlock_bh(&stats->lock); +#if 0 + bes2600_dbg(BES2600_DBG_TXRX, "queue_get queue %d, %d, %d\n", + queue->num_queued, + queue->link_map_cache[item->txpriv.if_id][item->txpriv.link_id], + queue->num_pending); + bes2600_dbg(BES2600_DBG_TXRX, "queue_get stats %d, %d\n", stats->num_queued, + stats->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]); +#endif + } else { + bes2600_warn(BES2600_DBG_TXRX, "%s: queue get failed, if_id = %d, link_id_map = %u\n", __func__, if_id, link_id_map); + } + + spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return ret; +} + +#ifdef CONFIG_BES2600_TESTMODE +int bes2600_queue_requeue(struct bes2600_common *hw_priv, + struct bes2600_queue *queue, u32 packetID, bool check) +#else +int bes2600_queue_requeue(struct bes2600_queue *queue, u32 packetID, bool check) +#endif +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct bes2600_queue_item *item; + struct bes2600_queue_stats *stats = queue->stats; + + bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + + item = &queue->pool[item_id]; +#ifdef P2P_MULTIVIF + if (check && item->txpriv.if_id == CW12XX_GENERIC_IF_ID) { +#else + if (check && item->txpriv.offchannel_if_id == CW12XX_GENERIC_IF_ID) { +#endif + bes2600_dbg(BES2600_DBG_TXRX, "Requeued frame dropped for " + "generic interface id.\n"); +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, packetID); +#else + bes2600_queue_remove(queue, packetID); +#endif + return 0; + } + +#ifndef P2P_MULTIVIF + if (!check) + item->txpriv.offchannel_if_id = CW12XX_GENERIC_IF_ID; +#endif + + /*if_id = item->txpriv.if_id;*/ + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__); + ret = 0; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + --queue->num_pending_vif[if_id]; + ++queue->link_map_cache[if_id][item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued[item->txpriv.if_id]; + ++stats->link_map_cache[if_id][item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + item->generation = ++item_generation; + item->packetID = bes2600_queue_make_packet_id( + queue_generation, queue_id, item_generation, item_id, + if_id, link_id); + list_move(&item->head, &queue->queue); +#if 0 + bes2600_dbg(BES2600_DBG_TXRX, "queue_requeue queue %d, %d, %d\n", + queue->num_queued, + queue->link_map_cache[if_id][item->txpriv.link_id], + queue->num_pending); + bes2600_dbg(BES2600_DBG_TXRX, "queue_requeue stats %d, %d\n", + stats->num_queued, + stats->link_map_cache[if_id][item->txpriv.link_id]); +#endif + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int bes2600_sw_retry_requeue(struct bes2600_common *hw_priv, + struct bes2600_queue *queue, u32 packetID, bool check) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct bes2600_queue_item *item; + struct bes2600_queue_stats *stats = queue->stats; + bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + item = &queue->pool[item_id]; +#ifdef P2P_MULTIVIF + if (check && item->txpriv.if_id == CW12XX_GENERIC_IF_ID) { +#else + if (check && item->txpriv.offchannel_if_id == CW12XX_GENERIC_IF_ID) { +#endif + bes2600_dbg(BES2600_DBG_TXRX, "Requeued frame dropped for " + "generic interface id.\n"); +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, packetID); +#else + bes2600_queue_remove(queue, packetID); +#endif + return 0; + } + +#ifndef P2P_MULTIVIF + if (!check) + item->txpriv.offchannel_if_id = CW12XX_GENERIC_IF_ID; +#endif + /*if_id = item->txpriv.if_id;*/ + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__); + ret = 0; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + --queue->num_pending_vif[if_id]; + ++queue->link_map_cache[if_id][item->txpriv.link_id]; + spin_lock_bh(&stats->lock); + ++stats->num_queued[item->txpriv.if_id]; + ++stats->link_map_cache[if_id][item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + item->packetID = bes2600_queue_make_packet_id( + queue_generation, queue_id, item_generation, item_id, + if_id, link_id); + + list_move(&item->head, &queue->queue); + + } + + spin_unlock_bh(&queue->lock); + return ret; +} + + +int bes2600_queue_requeue_all(struct bes2600_queue *queue) +{ + struct bes2600_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); + while (!list_empty(&queue->pending)) { + struct bes2600_queue_item *item = list_entry( + queue->pending.prev, struct bes2600_queue_item, head); + + --queue->num_pending; + --queue->num_pending_vif[item->txpriv.if_id]; + ++queue->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued[item->txpriv.if_id]; + ++stats->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + ++item->generation; + item->packetID = bes2600_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool, + item->txpriv.if_id, item->txpriv.raw_link_id); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + + return 0; +} +#ifdef CONFIG_BES2600_TESTMODE +int bes2600_queue_remove(struct bes2600_common *hw_priv, + struct bes2600_queue *queue, u32 packetID) +#else +int bes2600_queue_remove(struct bes2600_queue *queue, u32 packetID) +#endif /*CONFIG_BES2600_TESTMODE*/ +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct bes2600_queue_item *item; + struct bes2600_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct bes2600_txpriv gc_txpriv; + + bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + /*TODO:COMBO:Add check for interface ID also */ + if (unlikely(queue_generation != queue->generation)) { + bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__); + ret = 0; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; + --queue->num_pending; + --queue->num_pending_vif[if_id]; + --queue->num_queued; + --queue->num_queued_vif[if_id]; + ++queue->num_sent; + ++item->generation; +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->start_stop_tsm.start) { + if (queue_id == hw_priv->tsm_info.ac) { + struct timeval tmval; + unsigned long queue_delay; + unsigned long media_delay; + do_gettimeofday(&tmval); + + if (tmval.tv_usec > item->qdelay_timestamp) + queue_delay = tmval.tv_usec - + item->qdelay_timestamp; + else + queue_delay = tmval.tv_usec + + 1000000 - item->qdelay_timestamp; + + if (tmval.tv_usec > item->mdelay_timestamp) + media_delay = tmval.tv_usec - + item->mdelay_timestamp; + else + media_delay = tmval.tv_usec + + 1000000 - item->mdelay_timestamp; + hw_priv->tsm_info.sum_media_delay += + media_delay; + hw_priv->tsm_info.sum_pkt_q_delay += queue_delay; + if (queue_delay <= 10000) + hw_priv->tsm_stats.bin0++; + else if (queue_delay <= 20000) + hw_priv->tsm_stats.bin1++; + else if (queue_delay <= 40000) + hw_priv->tsm_stats.bin2++; + else + hw_priv->tsm_stats.bin3++; + } + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (unlikely(queue->overfull) && + (queue->num_queued <= ((stats->hw_priv->vif0_throttle + stats->hw_priv->vif1_throttle + 2) / 2))) { + queue->overfull = false; + __bes2600_queue_unlock(queue); + } + } + spin_unlock_bh(&queue->lock); + +#if 0 + bes2600_dbg(BES2600_DBG_TXRX, "queue_drop queue %d, %d, %d\n", + queue->num_queued, queue->link_map_cache[if_id][0], + queue->num_pending); + bes2600_dbg(BES2600_DBG_TXRX, "queue_drop stats %d, %d\n", stats->num_queued, + stats->link_map_cache[if_id][0]); +#endif + if (gc_skb) + stats->skb_dtor(stats->hw_priv, gc_skb, &gc_txpriv); + + return ret; +} + +int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID, + struct sk_buff **skb, + const struct bes2600_txpriv **txpriv) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct bes2600_queue_item *item; + + bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + /* TODO:COMBO: Add check for interface ID here */ + if (unlikely(queue_generation != queue->generation)) { + bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__); + ret = -EINVAL; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + bes2600_info(BES2600_DBG_TXRX, "%s, item_generation =%u, item_id =%u link_id=%u queue_generation =%u packetID =%u\n", + __func__, item_generation, item_id, link_id, queue_generation, packetID); + WARN_ON(1); + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +void bes2600_queue_lock(struct bes2600_queue *queue) +{ + spin_lock_bh(&queue->lock); + __bes2600_queue_lock(queue); + spin_unlock_bh(&queue->lock); +} + +void bes2600_queue_unlock(struct bes2600_queue *queue) +{ + spin_lock_bh(&queue->lock); + __bes2600_queue_unlock(queue); + spin_unlock_bh(&queue->lock); +} + +bool bes2600_queue_get_xmit_timestamp(struct bes2600_queue *queue, + unsigned long *timestamp, int if_id, + u32 pending_frameID) +{ + struct bes2600_queue_item *item; + bool ret; + + spin_lock_bh(&queue->lock); + ret = !list_empty(&queue->pending); + if (ret) { + list_for_each_entry(item, &queue->pending, head) { + if (((if_id == CW12XX_GENERIC_IF_ID) || + (if_id == CW12XX_ALL_IFS) || + (item->txpriv.if_id == if_id)) && + (item->packetID != pending_frameID)) { + if (time_before(item->xmit_timestamp, + *timestamp)) + *timestamp = item->xmit_timestamp; + } + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +bool bes2600_queue_stats_is_empty(struct bes2600_queue_stats *stats, + u32 link_id_map, int if_id) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) + empty = stats->num_queued[if_id] == 0; + else { + int i, if_id; + for (if_id = 0; if_id < CW12XX_MAX_VIFS; if_id++) { + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[if_id][i]) { + empty = false; + break; + } + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} + +void bes2600_queue_iterate_pending_packet(struct bes2600_queue *queue, + void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb)) +{ + struct bes2600_queue_item *item = NULL; + + if(list_empty(&queue->pending)) + return; + + list_for_each_entry(item, &queue->pending, head) { + iterate_cb(queue->stats->hw_priv, item->skb); + } +} \ No newline at end of file diff --git a/drivers/staging/bes2600/queue.h b/drivers/staging/bes2600/queue.h new file mode 100644 index 000000000000..803ceacff178 --- /dev/null +++ b/drivers/staging/bes2600/queue.h @@ -0,0 +1,173 @@ +/* + * O(1) TX queue with built-in allocator for BES2600 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_QUEUE_H_INCLUDED +#define BES2600_QUEUE_H_INCLUDED + +/* private */ struct bes2600_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct bes2600_common; +/* extern */ struct bes2600_vif; +/* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct bes2600_txpriv; + +/* forward */ struct bes2600_queue_stats; + +typedef void (*bes2600_queue_skb_dtor_t)(struct bes2600_common *priv, + struct sk_buff *skb, + const struct bes2600_txpriv *txpriv); + +struct bes2600_queue { + struct bes2600_queue_stats *stats; + size_t capacity; + size_t num_queued; + size_t num_queued_vif[CW12XX_MAX_VIFS]; + size_t num_pending; + size_t num_pending_vif[CW12XX_MAX_VIFS]; + size_t num_sent; + struct bes2600_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache[CW12XX_MAX_VIFS]; + bool overfull; + spinlock_t lock; + u8 queue_id; + u8 generation; + struct timer_list gc; + unsigned long ttl; +}; + +struct bes2600_queue_stats { + spinlock_t lock; + int *link_map_cache[CW12XX_MAX_VIFS]; + int num_queued[CW12XX_MAX_VIFS]; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; + bes2600_queue_skb_dtor_t skb_dtor; + struct bes2600_common *hw_priv; +}; + +struct bes2600_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; + u8 rate_id; + u8 offset; + u8 if_id; +#ifndef P2P_MULTIVIF + u8 offchannel_if_id; +#else + u8 raw_if_id; +#endif + u8 retry_count; +}; + +int bes2600_queue_stats_init(struct bes2600_queue_stats *stats, + size_t map_capacity, + bes2600_queue_skb_dtor_t skb_dtor, + struct bes2600_common *priv); +int bes2600_queue_init(struct bes2600_queue *queue, + struct bes2600_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl); +int bes2600_queue_clear(struct bes2600_queue *queue, int if_id); +void bes2600_queue_stats_deinit(struct bes2600_queue_stats *stats); +void bes2600_queue_deinit(struct bes2600_queue *queue); + +size_t bes2600_queue_get_num_queued(struct bes2600_vif *priv, + struct bes2600_queue *queue, + u32 link_id_map); +int bes2600_queue_put(struct bes2600_queue *queue, + struct sk_buff *skb, + struct bes2600_txpriv *txpriv); +int bes2600_queue_get(struct bes2600_queue *queue, + int if_id, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + struct bes2600_txpriv **txpriv); +#ifdef CONFIG_BES2600_TESTMODE +int bes2600_queue_requeue(struct bes2600_common *hw_priv, + struct bes2600_queue *queue, + u32 packetID, bool check); +#else +int bes2600_queue_requeue(struct bes2600_queue *queue, u32 packetID, bool check); +#endif +int bes2600_queue_requeue_all(struct bes2600_queue *queue); +#ifdef CONFIG_BES2600_TESTMODE +int bes2600_queue_remove(struct bes2600_common *hw_priv, + struct bes2600_queue *queue, + u32 packetID); +#else +int bes2600_queue_remove(struct bes2600_queue *queue, + u32 packetID); +#endif /*CONFIG_BES2600_TESTMODE*/ +int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID, + struct sk_buff **skb, + const struct bes2600_txpriv **txpriv); +void bes2600_queue_lock(struct bes2600_queue *queue); +void bes2600_queue_unlock(struct bes2600_queue *queue); +bool bes2600_queue_get_xmit_timestamp(struct bes2600_queue *queue, + unsigned long *timestamp, int if_id, + u32 pending_frameID); + + +bool bes2600_queue_stats_is_empty(struct bes2600_queue_stats *stats, + u32 link_id_map, int if_id); + +static inline void bes2600_queue_parse_id(u32 packetID, u8 *queue_generation, + u8 *queue_id, + u8 *item_generation, + u8 *item_id, + u8 *if_id, + u8 *link_id) +{ + *item_id = (packetID >> 0) & 0xFF; + *item_generation = (packetID >> 8) & 0xFF; + *queue_id = (packetID >> 16) & 0xF; + *if_id = (packetID >> 20) & 0xF; + *link_id = (packetID >> 24) & 0xF; + *queue_generation = (packetID >> 28) & 0xF; +} +static inline u8 bes2600_queue_get_queue_id(u32 packetID) +{ + return (packetID >> 16) & 0xF; +} + +static inline u8 bes2600_queue_get_if_id(u32 packetID) +{ + return (packetID >> 20) & 0xF; +} + +static inline u8 bes2600_queue_get_link_id(u32 packetID) +{ + return (packetID >> 24) & 0xF; +} + +static inline u8 bes2600_queue_get_generation(u32 packetID) +{ + return (packetID >> 8) & 0xFF; +} + +int bes2600_queue_get_skb_and_timestamp(struct bes2600_queue *queue, u32 packetID, + struct sk_buff **skb, struct bes2600_txpriv **txpriv, + unsigned long *timestamp); +int bes2600_sw_retry_requeue(struct bes2600_common *hw_priv, + struct bes2600_queue *queue, u32 packetID, bool check); +void bes2600_queue_iterate_pending_packet(struct bes2600_queue *queue, + void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb)); + +#endif /* BES2600_QUEUE_H_INCLUDED */ diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h new file mode 100644 index 000000000000..d14c76698c60 --- /dev/null +++ b/drivers/staging/bes2600/sbus.h @@ -0,0 +1,105 @@ +/* + * Common sbus abstraction layer interface for bes2600 wireless driver + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_SBUS_H +#define BES2600_SBUS_H + +/* + * sbus priv forward definition. + * Implemented and instantiated in particular modules. + */ +struct sbus_priv; +struct bes2600_common; + +typedef void (*sbus_irq_handler)(void *priv); + +enum SUBSYSTEM { + SUBSYSTEM_MCU = 0, + SUBSYSTEM_WIFI, + SUBSYSTEM_BT, + SUBSYSTEM_BT_LP, +}; + +enum GPIO_WAKE_FLAG +{ + GPIO_WAKE_FLAG_MCU = 0, + GPIO_WAKE_FLAG_WIFI_ON, + GPIO_WAKE_FLAG_WIFI_OFF, + GPIO_WAKE_FLAG_BT_ON, + GPIO_WAKE_FLAG_BT_OFF, + GPIO_WAKE_FLAG_BT_LP_ON, + GPIO_WAKE_FLAG_BT_LP_OFF, + GPIO_WAKE_FLAG_HOST_SUSPEND, + GPIO_WAKE_FLAG_HOST_RESUME, + GPIO_WAKE_FLAG_SDIO_RX, + GPIO_WAKE_FLAG_SDIO_PROBE, +}; + +struct sbus_ops { + int (*init)(struct sbus_priv *self, struct bes2600_common *ar); + int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr, + void *dst, int count); + int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct sbus_priv *self); + void (*unlock)(struct sbus_priv *self); + int (*irq_subscribe)(struct sbus_priv *self, sbus_irq_handler handler, + void *priv); + int (*irq_unsubscribe)(struct sbus_priv *self); + int (*reset)(struct sbus_priv *self); + size_t (*align_size)(struct sbus_priv *self, size_t size); + int (*set_block_size)(struct sbus_priv *self, size_t size); + int (*pipe_send)(struct sbus_priv *self, u8 pipe, u32 len, u8 *buf); + void * (*pipe_read)(struct sbus_priv *self); + int (*sbus_reg_read)(struct sbus_priv *self, u32 reg, + void *buf, int count); + int (*sbus_reg_write)(struct sbus_priv *self, u32 reg, + const void *buf, int count); + + /* sub_system: 0 for mcu, 1 for wifi, 2 for bt, ... */ + int (*sbus_active)(struct sbus_priv *self, int sub_system); + int (*sbus_deactive)(struct sbus_priv *self, int sub_system); + int (*power_switch)(struct sbus_priv *self, int on); + /* gpio wake, beacuse bes2600 sdio can't wakeup mcu, so add the two of interfaces */ + void (*gpio_wake)(struct sbus_priv *self, int falg); + void (*gpio_sleep)(struct sbus_priv *self, int falg); + /* halt device to get debug information */ + void (*halt_device)(struct sbus_priv *self); + bool (*wakeup_source)(struct sbus_priv *self); +}; + +#ifdef CONFIG_BES2600_WLAN_USB +/* tx/rx pipes for usb */ +enum BES2600_USB_PIPE_ID { + BES2600_USB_PIPE_TX_CTRL = 0, + BES2600_USB_PIPE_TX_WLAN, + BES2600_USB_PIPE_TX_BT, + BES2600_USB_PIPE_RX_CTRL, + BES2600_USB_PIPE_RX_WLAN, + BES2600_USB_PIPE_RX_BT, + BES2600_USB_PIPE_MAX +}; + +// virtual register definition +#define BES_USB_CONTROL_REG 0 +#define BES_USB_STATUS_REG 1 + +// virtual register bits definition +#define BES_USB_FW_TX_DONE BIT(0) +#define BES_USB_FW_RX_INDICATION BIT(1) +#endif + +void bes2600_irq_handler(struct bes2600_common *priv); + +/* This MUST be wrapped with hwbus_ops->lock/unlock! */ +int __bes2600_irq_enable(int enable); + +#endif /* BES2600_SBUS_H */ diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c new file mode 100644 index 000000000000..e0e13b3e5a0f --- /dev/null +++ b/drivers/staging/bes2600/scan.c @@ -0,0 +1,1180 @@ +/* + * Scan implementation for BES2600 mac80211 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "bes2600.h" +#include "scan.h" +#include "sta.h" +#include "pm.h" +#include "epta_request.h" +#include "bes_pwr.h" + +static void bes2600_scan_restart_delayed(struct bes2600_vif *priv); + +#ifdef CONFIG_BES2600_TESTMODE +static int bes2600_advance_scan_start(struct bes2600_common *hw_priv) +{ + int tmo = 0; + tmo += hw_priv->advanceScanElems.duration; + bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_ADV_SCAN, tmo); + /* Invoke Advance Scan Duration Timeout Handler */ + queue_delayed_work(hw_priv->workqueue, + &hw_priv->advance_scan_timeout, tmo * HZ / 1000); + return 0; +} +#endif + +static void bes2600_remove_wps_p2p_ie(struct wsm_template_frame *frame) +{ + u8 *ies; + u32 ies_len; + u32 ie_len; + u32 p2p_ie_len = 0; + u32 wps_ie_len = 0; + + ies = &frame->skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame->skb->len - sizeof(struct ieee80211_hdr_3addr); + + while (ies_len >= 6) { + ie_len = ies[1] + 2; + if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) + && (ies[2] == 0x00 && ies[3] == 0x50 && ies[4] == 0xf2 && ies[5] == 0x04)) { + wps_ie_len = ie_len; + memmove(ies, ies + ie_len, ies_len); + ies_len -= ie_len; + + } + else if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) && + (ies[2] == 0x50 && ies[3] == 0x6f && ies[4] == 0x9a && ies[5] == 0x09)) { + p2p_ie_len = ie_len; + memmove(ies, ies + ie_len, ies_len); + ies_len -= ie_len; + } else { + ies += ie_len; + ies_len -= ie_len; + } + } + + if (p2p_ie_len || wps_ie_len) { + skb_trim(frame->skb, frame->skb->len - (p2p_ie_len + wps_ie_len)); + } +} + +#ifdef CONFIG_BES2600_TESTMODE +static int bes2600_disable_filtering(struct bes2600_vif *priv) +{ + int ret = 0; + bool bssid_filtering = 0; + struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_control bf_control; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + /* RX Filter Disable */ + rx_filter.promiscuous = 0; + rx_filter.bssid = 0; + rx_filter.fcs = 0; + rx_filter.probeResponder = 0; + rx_filter.keepalive = 0; + ret = wsm_set_rx_filter(hw_priv, &rx_filter, + priv->if_id); + + /* Beacon Filter Disable */ + bf_control.enabled = 0; + bf_control.bcn_count = 1; + if (!ret) + ret = wsm_beacon_filter_control(hw_priv, &bf_control, + priv->if_id); + + /* BSSID Filter Disable */ + if (!ret) + ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, + priv->if_id); + + return ret; +} +#endif + +static int bes2600_scan_get_first_active_if(struct bes2600_common *hw_priv) +{ + int i = 0; + struct bes2600_vif *vif; + + bes2600_for_each_vif(hw_priv, vif, i) { + if (vif->join_status > BES2600_JOIN_STATUS_PASSIVE) + return i; + } + + return -1; +} + +static int bes2600_scan_start(struct bes2600_vif *priv, struct wsm_scan *scan) +{ + int ret, i; +#ifdef FPGA_SETUP + int tmo = 5000; +#else + int tmo = 5000; +#endif + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + + if (hw_priv->scan_switch_if_id == -1 && + hw_priv->ht_info.channel_type > NL80211_CHAN_HT20 && + priv->if_id >= 0) { + hw_priv->scan_switch_if_id = bes2600_scan_get_first_active_if(hw_priv); + if(hw_priv->scan_switch_if_id >= 0) { + struct wsm_switch_channel channel; + channel.channelMode = 0 << 4; + channel.channelSwitchCount = 0; + channel.newChannelNumber = hw_priv->channel->hw_value; + wsm_switch_channel(hw_priv, &channel, hw_priv->scan_switch_if_id); + bes2600_info(BES2600_DBG_SCAN, "scan start channel type %d num %d\n", hw_priv->ht_info.channel_type, channel.newChannelNumber); + } + } + for (i = 0; i < scan->numOfChannels; ++i) + tmo += scan->ch[i].maxChannelTime + 10; + atomic_set(&hw_priv->scan.in_progress, 1); + atomic_set(&hw_priv->recent_scan, 1); + queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, + tmo * HZ / 1000); +#ifdef P2P_MULTIVIF + ret = wsm_scan(hw_priv, scan, 0); +#else + ret = wsm_scan(hw_priv, scan, priv->if_id); +#endif + if (unlikely(ret)) { + atomic_set(&hw_priv->scan.in_progress, 0); + cancel_delayed_work_sync(&hw_priv->scan.timeout); + bes2600_scan_restart_delayed(priv); + } + return ret; +} + +#ifdef ROAM_OFFLOAD +static int bes2600_sched_scan_start(struct bes2600_vif *priv, struct wsm_scan *scan) +{ + int ret; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + ret = wsm_scan(hw_priv, scan, priv->if_id); + if (unlikely(ret)) { + atomic_set(&hw_priv->scan.in_progress, 0); + } + return ret; +} +#endif /*ROAM_OFFLOAD*/ + +int bes2600_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct cfg80211_scan_request *req = &hw_req->req; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i; + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == BES2600_JOIN_STATUS_AP) + return -EOPNOTSUPP; +#if 0 + if (work_pending(&priv->offchannel_work) || + (hw_priv->roc_if_id != -1)) { + wiphy_dbg(hw_priv->hw->wiphy, "[SCAN] Offchannel work pending, " + "ignoring scan work %d\n", hw_priv->roc_if_id); + return -EBUSY; + } +#endif + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > hw->wiphy->max_scan_ssids) + return -EINVAL; + + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN); + + frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, + req->ie_len); + if (!frame.skb) + return -ENOMEM; + + if (req->ie_len) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + skb_put_data(frame.skb, req->ie, req->ie_len); +#else + memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); +#endif + + /* will be unlocked in bes2600_scan_work() */ + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + + if (frame.skb) { + int ret; + //if (priv->if_id == 0) + // bes2600_remove_wps_p2p_ie(&frame); +#ifdef P2P_MULTIVIF + ret = wsm_set_template_frame(hw_priv, &frame, 0); +#else + ret = wsm_set_template_frame(hw_priv, &frame, + priv->if_id); +#endif +#if 0 + if (!ret) { + /* Host want to be the probe responder. */ + ret = wsm_set_probe_responder(priv, true); + } +#endif + if (ret) { + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + } + + wsm_vif_lock_tx(priv); + + BUG_ON(hw_priv->scan.req); + hw_priv->scan.req = req; + hw_priv->scan.n_ssids = 0; + hw_priv->scan.status = 0; + hw_priv->scan.begin = &req->channels[0]; + hw_priv->scan.curr = hw_priv->scan.begin; + hw_priv->scan.end = &req->channels[req->n_channels]; + hw_priv->scan.output_power = hw_priv->output_power; + hw_priv->scan.if_id = priv->if_id; + /* TODO:COMBO: Populate BIT4 in scanflags to decide on which MAC + * address the SCAN request will be sent */ + bes2600_info(BES2600_DBG_SCAN, "%s %d if_id:%d,num_channel:%d.\n", __func__, __LINE__, priv->if_id, req->n_channels); + + for (i = 0; i < req->n_ssids; ++i) { + struct wsm_ssid *dst = + &hw_priv->scan.ssids[hw_priv->scan.n_ssids]; + BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, + sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++hw_priv->scan.n_ssids; + } + + up(&hw_priv->conf_lock); + + if (frame.skb) + dev_kfree_skb(frame.skb); +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING); +#endif + queue_work(hw_priv->workqueue, &hw_priv->scan.work); + + return 0; +} + +#ifdef ROAM_OFFLOAD +int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i; + + wiphy_warn(hw->wiphy, "[SCAN] Scheduled scan request-->.\n"); + + if (!priv->vif) + return -EINVAL; + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == BES2600_JOIN_STATUS_AP) + return -EOPNOTSUPP; + + wiphy_warn(hw->wiphy, "[SCAN] Scheduled scan: n_ssids %d, ssid[0].len = %d\n", req->n_ssids, req->ssids[0].ssid_len); + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > hw->wiphy->max_scan_ssids) + return -EINVAL; + + frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, + req->ie_len); + if (!frame.skb) + return -ENOMEM; + + /* will be unlocked in bes2600_scan_work() */ + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + if (frame.skb) { + int ret; + if (priv->if_id == 0) + bes2600_remove_wps_p2p_ie(&frame); + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + if (0 == ret) { + /* Host want to be the probe responder. */ + ret = wsm_set_probe_responder(priv, true); + } + if (ret) { + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + } + + wsm_lock_tx(hw_priv); + + BUG_ON(hw_priv->scan.req); + hw_priv->scan.sched_req = req; + hw_priv->scan.n_ssids = 0; + hw_priv->scan.status = 0; + hw_priv->scan.begin = &req->channels[0]; + hw_priv->scan.curr = hw_priv->scan.begin; + hw_priv->scan.end = &req->channels[req->n_channels]; + hw_priv->scan.output_power = hw_priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + struct wsm_ssid *dst = + &hw_priv->scan.ssids[hw_priv->scan.n_ssids]; + BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, + sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++hw_priv->scan.n_ssids; + { + u8 j; + wiphy_warn(hw->wiphy, "[SCAN] SSID %d\n",i); + for(j=0; jssids[i].ssid_len; j++) + wiphy_warn(priv->hw->wiphy, "[SCAN] 0x%x\n", req->ssids[i].ssid[j]); + } + } + + up(&hw_priv->conf_lock); + + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(hw_priv->workqueue, &hw_priv->scan.swork); + wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n"); + return 0; +} +#endif /*ROAM_OFFLOAD*/ +void bes2600_scan_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = container_of(work, + struct bes2600_common, + scan.work); + struct bes2600_vif *priv, *vif; + struct ieee80211_channel **it; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .scanFlags = 0, /* TODO:COMBO */ + //.scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, /* TODO:COMBO */ + }; + bool first_run; + int i; + const u32 ProbeRequestTime = 2; + const u32 ChannelRemainTime = 15; +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + u32 minChannelTime; +#endif + u32 maxChannelTime; +#ifdef CONFIG_BES2600_TESTMODE + int ret = 0; + u16 advance_scan_req_channel = hw_priv->scan.begin[0]->hw_value; +#endif + priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + + /*TODO: COMBO: introduce locking so vif is not removed in meanwhile */ + if (!priv) { + wiphy_warn(hw_priv->hw->wiphy, "[SCAN] interface removed, " + "ignoring scan work\n"); + return; + } + + if (priv->if_id) + scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1; + else + scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1; + + bes2600_for_each_vif(hw_priv, vif, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !vif) +#else + if (!vif) +#endif + continue; + if (vif->bss_loss_status > BES2600_BSS_LOSS_NONE) + scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + first_run = hw_priv->scan.begin == hw_priv->scan.curr && + hw_priv->scan.begin != hw_priv->scan.end; + + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) { + bes2600_join_timeout(&priv->join_timeout.work); + } + } + down(&hw_priv->conf_lock); + if (first_run) { +#ifdef CONFIG_BES2600_TESTMODE + /* Passive Scan - Serving Channel Request Handling */ + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == BES2600_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == + advance_scan_req_channel)) { + /* If Advance Scan Request is for Serving Channel Device + * should be Active and Filtering Should be Disable */ + if (priv->powersave_mode.pmMode & WSM_PSM_PS) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_ACTIVE; + wsm_set_pm(hw_priv, &pm, priv->if_id); + } + /* Disable Rx Beacon and Bssid filter */ + ret = bes2600_disable_filtering(priv); + if (ret) + wiphy_err(hw_priv->hw->wiphy, + "%s: Disable BSSID or Beacon filtering failed: %d.\n", + __func__, ret); + } else if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == BES2600_JOIN_STATUS_STA)) { + if (priv->join_status == BES2600_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + bes2600_set_pm(priv, &pm); + } + } else { +#endif +#if 0 + if (priv->join_status == BES2600_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + bes2600_set_pm(priv, &pm); + } else +#endif + if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan */ + bes2600_disable_listening(priv); + } +#ifdef CONFIG_BES2600_TESTMODE + } +#endif + } + + if (!hw_priv->scan.req || (hw_priv->scan.curr == hw_priv->scan.end)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + struct cfg80211_scan_info info = { + .aborted = hw_priv->scan.status ? 1 : 0, + }; +#else + bool aborted = hw_priv->scan.status ? true : false; +#endif + +#ifdef CONFIG_BES2600_TESTMODE + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == BES2600_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == + advance_scan_req_channel)) { + /* WSM Lock should be held here for WSM APIs */ + wsm_vif_lock_tx(priv); + /* wsm_lock_tx(priv); */ + /* Once Duration is Over, enable filtering + * and Revert Back Power Save */ + if (priv->powersave_mode.pmMode & WSM_PSM_PS) + wsm_set_pm(hw_priv, &priv->powersave_mode, + priv->if_id); + bes2600_update_filtering(priv); + } else { + if (!hw_priv->enable_advance_scan) { +#endif + if (hw_priv->scan.output_power != hw_priv->output_power) + /* TODO:COMBO: Change when mac80211 implementation + * is available for output power also */ +#ifdef P2P_MULTIVIF + WARN_ON(wsm_set_output_power(hw_priv, + hw_priv->output_power * 10, + priv->if_id ? 0 : 0)); +#else + WARN_ON(wsm_set_output_power(hw_priv, + hw_priv->output_power * 10, + priv->if_id)); +#endif +#ifdef CONFIG_BES2600_TESTMODE + } + } +#endif +#if 0 + if (priv->join_status == BES2600_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) + bes2600_set_pm(priv, &priv->powersave_mode); +#endif + if (hw_priv->scan.status < 0) + wiphy_info(priv->hw->wiphy, + "[SCAN] Scan failed (%d).\n", + hw_priv->scan.status); + else if (hw_priv->scan.req) + wiphy_info(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); + else + wiphy_info(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + if (priv->join_status == BES2600_JOIN_STATUS_STA) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if (hw_priv->channel->band != NL80211_BAND_2GHZ) +#else + if (hw_priv->channel->band != IEEE80211_BAND_2GHZ) +#endif + bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP_5G); + else + bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP); + } else { + bwifi_change_current_status(hw_priv, BWIFI_STATUS_IDLE); + } +#endif + bes2600_info(BES2600_DBG_SCAN, "%s %d %d.", __func__, __LINE__, hw_priv->ht_info.channel_type); + /* switch to previous channel and bw mode after scan done */ + if (hw_priv->scan_switch_if_id >= 0) { + struct wsm_switch_channel channel; + channel.channelMode = hw_priv->ht_info.channel_type << 4; + channel.channelSwitchCount = 0; + channel.newChannelNumber = hw_priv->channel->hw_value; + wsm_switch_channel(hw_priv, &channel, hw_priv->scan_switch_if_id); + hw_priv->scan_switch_if_id = -1; + bes2600_info(BES2600_DBG_SCAN, "scan done channel type %d num %d\n", hw_priv->ht_info.channel_type, channel.newChannelNumber); + } + + hw_priv->scan.req = NULL; + bes2600_scan_restart_delayed(priv); +#ifdef CONFIG_BES2600_TESTMODE + hw_priv->enable_advance_scan = false; +#endif /* CONFIG_BES2600_TESTMODE */ + wsm_unlock_tx(hw_priv); + up(&hw_priv->conf_lock); + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + ieee80211_scan_completed(hw_priv->hw, &info); +#else + ieee80211_scan_completed(hw_priv->hw, aborted); +#endif + up(&hw_priv->scan.lock); + return; + } else { + struct ieee80211_channel *first = *hw_priv->scan.curr; + for (it = hw_priv->scan.curr + 1, i = 1; + it != hw_priv->scan.end && + i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + // Doen't split scan req in case of EPTA error after scan req + // if (((*it)->flags ^ first->flags) & + // IEEE80211_CHAN_NO_IR) + // break; + // if (!(first->flags & IEEE80211_CHAN_NO_IR) && + // (*it)->max_power != first->max_power) + // break; + } + scan.band = first->band; + + if (hw_priv->scan.req->no_cck) + scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; + else + scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; +#ifdef CONFIG_BES2600_TESTMODE + if (hw_priv->enable_advance_scan) { + if (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_PASSIVE) + scan.numOfProbeRequests = 0; + else + scan.numOfProbeRequests = 2; + } else { +#endif + /* TODO: Is it optimal? */ + scan.numOfProbeRequests = + (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; +#ifdef CONFIG_BES2600_TESTMODE + } +#endif /* CONFIG_BES2600_TESTMODE */ + scan.numOfSSIDs = hw_priv->scan.n_ssids; + scan.ssids = &hw_priv->scan.ssids[0]; + scan.numOfChannels = it - hw_priv->scan.curr; + /* TODO: Is it optimal? */ + scan.probeDelay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. */ + if (priv->join_status == BES2600_JOIN_STATUS_STA) { + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + scan.ch = kzalloc((it - hw_priv->scan.curr) * + sizeof(struct wsm_scan_ch), GFP_KERNEL); + if (!scan.ch) { + hw_priv->scan.status = -ENOMEM; + goto fail; + } + maxChannelTime = (scan.numOfSSIDs * scan.numOfProbeRequests * + ProbeRequestTime) + ChannelRemainTime; + maxChannelTime = (maxChannelTime < 35) ? 35 : maxChannelTime; + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + if (scan.band == NL80211_BAND_2GHZ) { + coex_calc_wifi_scan_time(&minChannelTime, &maxChannelTime); + } else { + minChannelTime = 90; + maxChannelTime = 90; + } +#endif + + for (i = 0; i < scan.numOfChannels; ++i) { + scan.ch[i].number = hw_priv->scan.curr[i]->hw_value; +#ifdef CONFIG_BES2600_TESTMODE + if (hw_priv->enable_advance_scan) { + scan.ch[i].minChannelTime = + hw_priv->advanceScanElems.duration; + scan.ch[i].maxChannelTime = + hw_priv->advanceScanElems.duration; + } else { +#endif + +#ifndef WIFI_BT_COEXIST_EPTA_ENABLE + if (hw_priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { + scan.ch[i].minChannelTime = 40; + scan.ch[i].maxChannelTime = 100; + } + else { + //TODO: modify maxChannelTime + scan.ch[i].minChannelTime = 15; + scan.ch[i].maxChannelTime = maxChannelTime; + } +#else + scan.ch[i].minChannelTime = minChannelTime; + scan.ch[i].maxChannelTime = maxChannelTime; +#endif + +#ifdef CONFIG_BES2600_TESTMODE + } +#endif + } +#ifdef CONFIG_BES2600_TESTMODE + if (!hw_priv->enable_advance_scan) { +#endif + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + hw_priv->scan.output_power != first->max_power) { + hw_priv->scan.output_power = first->max_power; + /* TODO:COMBO: Change after mac80211 implementation + * complete */ +#ifdef P2P_MULTIVIF + WARN_ON(wsm_set_output_power(hw_priv, + hw_priv->scan.output_power * 10, + priv->if_id ? 0 : 0)); +#else + WARN_ON(wsm_set_output_power(hw_priv, + hw_priv->scan.output_power * 10, + priv->if_id)); +#endif + } +#ifdef CONFIG_BES2600_TESTMODE + } +#endif +#ifdef CONFIG_BES2600_TESTMODE + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == BES2600_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == advance_scan_req_channel)) { + /* Start Advance Scan Timer */ + hw_priv->scan.status = bes2600_advance_scan_start(hw_priv); + wsm_unlock_tx(hw_priv); + } else +#endif + hw_priv->scan.status = bes2600_scan_start(priv, &scan); + kfree(scan.ch); + if (WARN_ON(hw_priv->scan.status)) + goto fail; + hw_priv->scan.curr = it; + } + up(&hw_priv->conf_lock); + return; + +fail: + hw_priv->scan.curr = hw_priv->scan.end; + up(&hw_priv->conf_lock); + queue_work(hw_priv->workqueue, &hw_priv->scan.work); + return; +} + +#ifdef ROAM_OFFLOAD +void bes2600_sched_scan_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = container_of(work, struct bes2600_common, + scan.swork); + struct wsm_scan scan; + struct wsm_ssid scan_ssid; + int i; + struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv, + hw_priv->scan.if_id); + if (unlikely(!priv)) { + WARN_ON(1); + return; + } + + spin_unlock(&priv->vif_lock); + + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) { + bes2600_join_timeout(&priv->join_timeout.work); + } + down(&hw_priv->conf_lock); + hw_priv->auto_scanning = 1; + + scan.band = 0; + + if (priv->join_status == BES2600_JOIN_STATUS_STA) + scan.scanType = 3; /* auto background */ + else + scan.scanType = 2; /* auto foreground */ + + scan.scanFlags = 0x01; /* bit 0 set => forced background scan */ + scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; + scan.autoScanInterval = (0xba << 24)|(30 * 1024); /* 30 seconds, -70 rssi */ + scan.numOfProbeRequests = 2; + //scan.numOfChannels = 11; + scan.numOfChannels = hw_priv->num_scanchannels; + scan.numOfSSIDs = 1; + scan.probeDelay = 100; + scan_ssid.length = priv->ssid_length; + memcpy(scan_ssid.ssid, priv->ssid, priv->ssid_length); + scan.ssids = &scan_ssid; + + scan.ch = kzalloc( + sizeof(struct wsm_scan_ch[scan.numOfChannels]), + GFP_KERNEL); + if (!scan.ch) { + hw_priv->scan.status = -ENOMEM; + goto fail; + } + + for (i = 0; i < scan.numOfChannels; i++) { + scan.ch[i].number = hw_priv->scan_channels[i].number; + scan.ch[i].minChannelTime = hw_priv->scan_channels[i].minChannelTime; + scan.ch[i].maxChannelTime = hw_priv->scan_channels[i].maxChannelTime; + scan.ch[i].txPowerLevel = hw_priv->scan_channels[i].txPowerLevel; + } + +#if 0 + for (i = 1; i <= scan.numOfChannels; i++) { + scan.ch[i-1].number = i; + scan.ch[i-1].minChannelTime = 10; + scan.ch[i-1].maxChannelTime = 40; + } +#endif + + hw_priv->scan.status = bes2600_sched_scan_start(priv, &scan); + kfree(scan.ch); + if (hw_priv->scan.status) + goto fail; + up(&hw_priv->conf_lock); + return; + +fail: + up(&hw_priv->conf_lock); + queue_work(hw_priv->workqueue, &hw_priv->scan.swork); + return; +} + +void bes2600_hw_sched_scan_stop(struct bes2600_common *hw_priv) +{ + struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv, + hw_priv->scan.if_id); + if (unlikely(!priv)) + return; + spin_unlock(&priv->vif_lock); + + wsm_stop_scan(hw_priv, priv->if_id); + + return; +} +#endif /*ROAM_OFFLOAD*/ + + +static void bes2600_scan_restart_delayed(struct bes2600_vif *priv) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + if (priv->delayed_link_loss) { + int tmo = priv->cqm_beacon_loss_count; + + if (hw_priv->scan.direct_probe) + tmo = 0; + + priv->delayed_link_loss = 0; + /* Restart beacon loss timer and requeue + BSS loss work. */ + wiphy_dbg(priv->hw->wiphy, + "[CQM] Requeue BSS loss in %d " + "beacons.\n", tmo); + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = BES2600_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + cancel_delayed_work_sync(&priv->bss_loss_work); + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, + tmo * HZ / 10); + } + + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) { + /*bes2600_enable_listening(priv);*/ + // WARN_ON(1); + bes2600_dbg(BES2600_DBG_SCAN, "scan complete join_status is monitor"); + bes2600_update_filtering(priv); + } + + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } +} + +static void bes2600_scan_complete(struct bes2600_common *hw_priv, int if_id) +{ + struct bes2600_vif *priv; + atomic_xchg(&hw_priv->recent_scan, 0); + + if (hw_priv->scan.direct_probe) { + down(&hw_priv->conf_lock); + priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id); + if (priv) { + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe " + "complete.\n"); + bes2600_scan_restart_delayed(priv); + } else { + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe " + "complete without interface!\n"); + } + up(&hw_priv->conf_lock); + hw_priv->scan.direct_probe = 0; + up(&hw_priv->scan.lock); + wsm_unlock_tx(hw_priv); + } else { + bes2600_scan_work(&hw_priv->scan.work); + } +} + +void bes2600_scan_complete_cb(struct bes2600_common *hw_priv, + struct wsm_scan_complete *arg) +{ + struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv, + hw_priv->scan.if_id); + + if (unlikely(!priv)) + return; + +#ifdef ROAM_OFFLOAD + if (hw_priv->auto_scanning) + queue_delayed_work(hw_priv->workqueue, + &hw_priv->scan.timeout, 0); +#endif /*ROAM_OFFLOAD*/ + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + spin_unlock(&priv->vif_lock); + return; + } + spin_unlock(&priv->vif_lock); + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + // recover EPTA timer after scan wsm msg complete, in case of epta state error + // bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING_COMP); +#endif + wiphy_info(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status); + + if(hw_priv->scan.status == -ETIMEDOUT) + wiphy_warn(hw_priv->hw->wiphy, + "Scan timeout already occured. Don't cancel work"); + if ((hw_priv->scan.status != -ETIMEDOUT) && + (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0)) { + hw_priv->scan.status = 1; + queue_delayed_work(hw_priv->workqueue, + &hw_priv->scan.timeout, 0); + } +} + +void bes2600_scan_timeout(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, scan.timeout.work); + + if (likely(atomic_xchg(&hw_priv->scan.in_progress, 0))) { + if (hw_priv->scan.status > 0) + hw_priv->scan.status = 0; + else if (!hw_priv->scan.status) { + wiphy_warn(hw_priv->hw->wiphy, + "Timeout waiting for scan " + "complete notification.\n"); + hw_priv->scan.status = -ETIMEDOUT; + hw_priv->scan.curr = hw_priv->scan.end; + WARN_ON(wsm_stop_scan(hw_priv, + hw_priv->scan.if_id ? 1 : 0)); + } + bes2600_scan_complete(hw_priv, hw_priv->scan.if_id); +#ifdef ROAM_OFFLOAD + } else if (hw_priv->auto_scanning) { + hw_priv->auto_scanning = 0; + ieee80211_sched_scan_results(hw_priv->hw); +#endif /*ROAM_OFFLOAD*/ + } +} + +#ifdef CONFIG_BES2600_TESTMODE +void bes2600_advance_scan_timeout(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, advance_scan_timeout.work); + + struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv, + hw_priv->scan.if_id); + if (WARN_ON(!priv)) + return; + spin_unlock(&priv->vif_lock); + + hw_priv->scan.status = 0; + if (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_PASSIVE) { + /* Passive Scan on Serving Channel + * Timer Expire */ + bes2600_scan_complete(hw_priv, hw_priv->scan.if_id); + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + struct cfg80211_scan_info info = { + .aborted = hw_priv->scan.status ? 1 : 0, + }; +#else + bool aborted = hw_priv->scan.status ? true : false; +#endif + /* Active Scan on Serving Channel + * Timer Expire */ + down(&hw_priv->conf_lock); + //wsm_lock_tx(priv); + wsm_vif_lock_tx(priv); + /* Once Duration is Over, enable filtering + * and Revert Back Power Save */ + if ((priv->powersave_mode.pmMode & WSM_PSM_PS)) + wsm_set_pm(hw_priv, &priv->powersave_mode, + priv->if_id); + hw_priv->scan.req = NULL; + bes2600_update_filtering(priv); + hw_priv->enable_advance_scan = false; + wsm_unlock_tx(hw_priv); + up(&hw_priv->conf_lock); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + ieee80211_scan_completed(hw_priv->hw, &info); +#else + ieee80211_scan_completed(hw_priv->hw, aborted); +#endif + up(&hw_priv->scan.lock); + } +} +#endif + +void bes2600_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + if(hw_priv->scan.if_id == priv->if_id) { + bes2600_dbg(BES2600_DBG_SCAN, "cancel hw_scan on intf:%d\n", priv->if_id); + + down(&hw_priv->conf_lock); + hw_priv->scan.req = NULL; + up(&hw_priv->conf_lock); + + /* cancel scan operation */ + wsm_stop_scan(hw_priv, priv->if_id); + + /* wait scan operation end */ + down(&hw_priv->scan.lock); + up(&hw_priv->scan.lock); + } +} + +void bes2600_probe_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, scan.probe_work.work); + struct bes2600_vif *priv, *vif; + u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id); + struct bes2600_queue *queue = &hw_priv->tx_queue[queueId]; + const struct bes2600_txpriv *txpriv; + struct wsm_tx *wsm; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .minChannelTime = 0, + .maxChannelTime = 10, + } }; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .numOfProbeRequests = 2, + .probeDelay = 0, + .numOfChannels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret = 1; + int i; + wiphy_info(hw_priv->hw->wiphy, "[SCAN] Direct probe work.\n"); + + BUG_ON(queueId >= 4); + BUG_ON(!hw_priv->channel); + + down(&hw_priv->conf_lock); + if (unlikely(down_trylock(&hw_priv->scan.lock))) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(hw_priv->workqueue, + &hw_priv->scan.probe_work, HZ / 10); + up(&hw_priv->conf_lock); + return; + } + + if (bes2600_queue_get_skb(queue, hw_priv->pending_frame_id, + &frame.skb, &txpriv)) { + up(&hw_priv->scan.lock); + up(&hw_priv->conf_lock); + wsm_unlock_tx(hw_priv); + return; + } + priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + if (!priv) { + up(&hw_priv->scan.lock); + up(&hw_priv->conf_lock); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.maxTransmitRate = wsm->maxTxRate; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + scan.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ? +#else + scan.band = (hw_priv->channel->band == IEEE80211_BAND_5GHZ) ? +#endif + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == BES2600_JOIN_STATUS_STA) { + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + if (priv->if_id) + scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1; + else + scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1; + } + bes2600_for_each_vif(hw_priv, vif, i) { + if (!vif) + continue; + if (vif->bss_loss_status > BES2600_BSS_LOSS_NONE) + scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + ch[0].number = hw_priv->channel->hw_value; + + skb_pull(frame.skb, txpriv->offset); + + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = + (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in bes2600_scan_start call */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.numOfSSIDs = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + if (priv->if_id == 0) + bes2600_remove_wps_p2p_ie(&frame); + + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) { + WARN_ON(1); + /*bes2600_disable_listening(priv);*/ + } + ret = WARN_ON(wsm_set_template_frame(hw_priv, &frame, + priv->if_id)); + + hw_priv->scan.direct_probe = 1; + hw_priv->scan.if_id = priv->if_id; + if (!ret) { + wsm_flush_tx(hw_priv); + ret = WARN_ON(bes2600_scan_start(priv, &scan)); + } + up(&hw_priv->conf_lock); + + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; +#ifdef CONFIG_BES2600_TESTMODE + BUG_ON(bes2600_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id)); +#else + BUG_ON(bes2600_queue_remove(queue, hw_priv->pending_frame_id)); +#endif + + if (ret) { + hw_priv->scan.direct_probe = 0; + up(&hw_priv->scan.lock); + wsm_unlock_tx(hw_priv); + } + + return; +} + diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h new file mode 100644 index 000000000000..e50fa363b84f --- /dev/null +++ b/drivers/staging/bes2600/scan.h @@ -0,0 +1,72 @@ +/* + * Scan interface for BES2600 mac80211 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +struct bes2600_scan { + struct semaphore lock; + struct work_struct work; +#ifdef ROAM_OFFLOAD + struct work_struct swork; /* scheduled scan work */ + struct cfg80211_sched_scan_request *sched_req; +#endif /*ROAM_OFFLOAD*/ + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + int direct_probe; + u8 if_id; +}; + +int bes2600_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req); +#ifdef ROAM_OFFLOAD +int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +void bes2600_hw_sched_scan_stop(struct bes2600_common *priv); +void bes2600_sched_scan_work(struct work_struct *work); +#endif /*ROAM_OFFLOAD*/ +void bes2600_scan_work(struct work_struct *work); +void bes2600_scan_timeout(struct work_struct *work); +void bes2600_scan_complete_cb(struct bes2600_common *priv, + struct wsm_scan_complete *arg); +void bes2600_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void bes2600_probe_work(struct work_struct *work); +#ifdef CONFIG_BES2600_TESTMODE +/* Advance Scan Timer */ +void bes2600_advance_scan_timeout(struct work_struct *work); +#endif + +#endif diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c new file mode 100644 index 000000000000..eed1c85fc67c --- /dev/null +++ b/drivers/staging/bes2600/sta.c @@ -0,0 +1,4514 @@ +/* + * Mac80211 STA API for BES2600 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "bes2600.h" +#include "sta.h" +#include "ap.h" +#include "fwio.h" +#include "bh.h" +#include "debug.h" +#include "wsm.h" +#ifdef ROAM_OFFLOAD +#include +#endif /*ROAM_OFFLOAD*/ +#ifdef CONFIG_BES2600_TESTMODE +#include "bes_nl80211_testmode_msg.h" +#include +#endif /* CONFIG_BES2600_TESTMODE */ +#include "net/mac80211.h" +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" + +#include "epta_request.h" +#include "epta_coex.h" + +#ifdef PLAT_ALLWINNER_R329 +#include "bes2600_factory.h" +#endif + +#include "txrx_opt.h" + +#define WEP_ENCRYPT_HDR_SIZE 4 +#define WEP_ENCRYPT_TAIL_SIZE 4 +#define WPA_ENCRYPT_HDR_SIZE 8 +#define WPA_ENCRYPT_TAIL_SIZE 12 +#define WPA2_ENCRYPT_HDR_SIZE 8 +#define WPA2_ENCRYPT_TAIL_SIZE 8 +#define WAPI_ENCRYPT_HDR_SIZE 18 +#define WAPI_ENCRYPT_TAIL_SIZE 16 +#define MAX_ARP_REPLY_TEMPLATE_SIZE 120 +#define MAX_TCP_ALIVE_TEMPLATE_SIZE 256 +#ifdef CONFIG_BES2600_TESTMODE +const int bes2600_1d_to_ac[8] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO +}; + +/** + * enum bes2600_ac_numbers - AC numbers as used in bes2600 + * @BES2600_AC_VO: voice + * @BES2600_AC_VI: video + * @BES2600_AC_BE: best effort + * @BES2600_AC_BK: background + */ +enum bes2600_ac_numbers { + BES2600_AC_VO = 0, + BES2600_AC_VI = 1, + BES2600_AC_BE = 2, + BES2600_AC_BK = 3, +}; + +int bes2600_testmode_reply(struct wiphy *wiphy, const void *data, int len); +#endif /*CONFIG_BES2600_TESTMODE*/ + +#ifdef IPV6_FILTERING +#define MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE 144 +#endif /*IPV6_FILTERING*/ + +static inline void __bes2600_free_event_queue(struct list_head *list) +{ + while (!list_empty(list)) { + struct bes2600_wsm_event *event = + list_first_entry(list, struct bes2600_wsm_event, + link); + list_del(&event->link); + kfree(event); + } +} + +#ifdef CONFIG_BES2600_TESTMODE +/* User priority to WSM queue mapping */ +const int bes2600_priority_to_queueId[8] = { + WSM_QUEUE_BEST_EFFORT, + WSM_QUEUE_BACKGROUND, + WSM_QUEUE_BACKGROUND, + WSM_QUEUE_BEST_EFFORT, + WSM_QUEUE_VIDEO, + WSM_QUEUE_VIDEO, + WSM_QUEUE_VOICE, + WSM_QUEUE_VOICE +}; +#endif /*CONFIG_BES2600_TESTMODE*/ +static inline void __bes2600_bf_configure(struct bes2600_vif *priv) +{ + priv->bf_table.numOfIEs = __cpu_to_le32(3); + priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC; + priv->bf_table.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + priv->bf_table.entry[0].oui[0] = 0x50; + priv->bf_table.entry[0].oui[1] = 0x6F; + priv->bf_table.entry[0].oui[2] = 0x9A; + + priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO; + priv->bf_table.entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + + priv->bf_table.entry[2].ieId = WLAN_EID_HT_OPERATION; + priv->bf_table.entry[2].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + + priv->bf_control.enabled = WSM_BEACON_FILTER_ENABLE; +} + +/* ******************************************************************** */ +/* STA API */ + +int bes2600_start(struct ieee80211_hw *dev) +{ + struct bes2600_common *hw_priv = dev->priv; + int ret = 0; + + bes2600_info(BES2600_DBG_STA, "bes2600_start\n"); + + if (!bes2600_chrdev_is_signal_mode()) { + return -1; + } + + bes2600_pwr_prepare(hw_priv); + + /* Assign Max SSIDs supported based on the firmware revision*/ + if (hw_priv->hw_revision <= BES2600_HW_REV_CUT22) { + dev->wiphy->max_scan_ssids = 2; /* for backward compatibility */ + } + down(&hw_priv->conf_lock); + +#ifndef CONFIG_BES2600_WLAN_USB +#ifdef CONFIG_BES2600_WLAN_BES + ret = bes2600_wifi_start(hw_priv); + if (ret) + goto out; +#endif +#endif + tx_policy_init(hw_priv); + + +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + memset(&hw_priv->tsm_stats, 0, sizeof(struct bes_tsm_stats)); + memset(&hw_priv->tsm_info, 0, sizeof(struct bes2600_tsm_info)); + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + memcpy(hw_priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + hw_priv->softled_state = 0; + + atomic_inc(&hw_priv->netdevice_start); +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + coex_start(hw_priv); +#endif + + bes2600_info(BES2600_DBG_STA, "%s %pM.\n", __func__, hw_priv->mac_addr); + ret = bes2600_setup_mac(hw_priv); + if (WARN_ON(ret)) + goto out; + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + bwifi_change_current_status(hw_priv, BWIFI_STATUS_IDLE); +#endif + +out: + up(&hw_priv->conf_lock); + if(ret != 0) + bes2600_pwr_complete(hw_priv); + return ret; +} + +void bes2600_stop(struct ieee80211_hw *dev) +{ + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_vif *priv = NULL; + LIST_HEAD(list); + int i; + + bes2600_info(BES2600_DBG_STA, "bes2600_stop\n"); + + atomic_dec(&hw_priv->netdevice_start); + + wsm_lock_tx(hw_priv); + + while (down_trylock(&hw_priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + hw_priv->scan.req = NULL; + schedule(); + } + up(&hw_priv->scan.lock); + + cancel_delayed_work_sync(&hw_priv->scan.probe_work); + cancel_delayed_work_sync(&hw_priv->scan.timeout); + +#ifdef CONFIG_BES2600_TESTMODE + cancel_delayed_work_sync(&hw_priv->advance_scan_timeout); +#endif + flush_workqueue(hw_priv->workqueue); + del_timer_sync(&hw_priv->ba_timer); + + down(&hw_priv->conf_lock); + + hw_priv->softled_state = 0; + /* bes2600_set_leds(hw_priv); */ + + spin_lock(&hw_priv->event_queue_lock); + list_splice_init(&hw_priv->event_queue, &list); + spin_unlock(&hw_priv->event_queue_lock); + __bes2600_free_event_queue(&list); + + for (i = 0; i < 4; i++) + bes2600_queue_clear(&hw_priv->tx_queue[i], CW12XX_ALL_IFS); + + /* HACK! */ + if (atomic_xchg(&hw_priv->tx_lock, 1) != 1) + bes2600_dbg(BES2600_DBG_STA, "[STA] TX is force-unlocked " + "due to stop request.\n"); + + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; + priv->delayed_link_loss = 0; + priv->join_status = BES2600_JOIN_STATUS_PASSIVE; + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + del_timer_sync(&priv->mcast_timeout); + } + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + cancel_work_sync(&hw_priv->coex_work); + coex_stop(hw_priv); +#endif + +#ifndef CONFIG_BES2600_WLAN_USB +#ifdef CONFIG_BES2600_WLAN_BES + bes2600_wifi_stop(hw_priv); +#endif +#endif + + tx_policy_deinit(hw_priv); + + wsm_unlock_tx(hw_priv); + + up(&hw_priv->conf_lock); + bes2600_pwr_complete(hw_priv); +} + + +int bes2600_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_vif *priv; + struct bes2600_vif **drv_priv = (void *)vif->drv_priv; +#ifndef P2P_MULTIVIF + int i; + + if (atomic_read(&hw_priv->num_vifs) >= CW12XX_MAX_VIFS) { + bes2600_err(BES2600_DBG_STA, "%s %d.",__func__, __LINE__); + return -EOPNOTSUPP; + } +#endif + bes2600_err(BES2600_DBG_STA, " !!! %s: type %d p2p %d addr %pM\n", + __func__, vif->type, vif->p2p, vif->addr); + + priv = cw12xx_get_vif_from_ieee80211(vif); + atomic_set(&priv->enabled, 0); + + *drv_priv = priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + down(&hw_priv->conf_lock); + + priv->mode = vif->type; + + spin_lock(&hw_priv->vif_list_lock); + if (atomic_read(&hw_priv->num_vifs) < CW12XX_MAX_VIFS) { +#ifdef P2P_MULTIVIF + if (!memcmp(vif->addr, hw_priv->addresses[0].addr, ETH_ALEN)) { + priv->if_id = 0; + } else if (!memcmp(vif->addr, hw_priv->addresses[1].addr, + ETH_ALEN)) { + priv->if_id = 2; + } else if (!memcmp(vif->addr, hw_priv->addresses[2].addr, + ETH_ALEN)) { + priv->if_id = 1; + } + bes2600_info(BES2600_DBG_STA, "%s: if_id %d mac %pM\n", + __func__, priv->if_id, vif->addr); +#else + for (i = 0; i < CW12XX_MAX_VIFS; i++) + if (!memcmp(vif->addr, hw_priv->addresses[i].addr, + ETH_ALEN)) + break; + if (i == CW12XX_MAX_VIFS) { + spin_unlock(&hw_priv->vif_list_lock); + up(&hw_priv->conf_lock); + return -EINVAL; + } + priv->if_id = i; +#endif + priv->hw_priv = hw_priv; + priv->hw = dev; + priv->vif = vif; + } else { + spin_unlock(&hw_priv->vif_list_lock); + up(&hw_priv->conf_lock); + return -EOPNOTSUPP; + } + spin_unlock(&hw_priv->vif_list_lock); + /* TODO:COMBO :Check if MAC address matches the one expected by FW */ + memcpy(hw_priv->mac_addr, vif->addr, ETH_ALEN); + + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + &auto_calibration_mode, sizeof(auto_calibration_mode))); + */ + bes2600_info(BES2600_DBG_STA, "[STA] Interface ID:%d of type:%d added\n", + priv->if_id, priv->mode); + + up(&hw_priv->conf_lock); + + bes2600_vif_setup(priv); + + ret = WARN_ON(bes2600_setup_mac_pvif(priv)); + + return ret; +} + +void bes2600_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct wsm_reset reset = { + .reset_statistics = true, + }; + int i; + bool is_htcapie = false; + struct bes2600_vif *tmp_priv; + + bes2600_info(BES2600_DBG_STA, " !!! %s: type %d p2p %d addr %pM\n", + __func__, vif->type, vif->p2p, vif->addr); + atomic_set(&priv->enabled, 0); + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + bes2600_tx_queues_lock(hw_priv); + wsm_lock_tx(hw_priv); + switch (priv->join_status) { + case BES2600_JOIN_STATUS_STA: + wsm_lock_tx(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + break; + case BES2600_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + cw12xx_unmap_link(priv, i); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, + sizeof(priv->link_id_db)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(hw_priv, &reset, priv->if_id); + bes2600_for_each_vif(hw_priv, tmp_priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv) +#else + if (!tmp_priv) +#endif + continue; + if ((tmp_priv->join_status == BES2600_JOIN_STATUS_STA) && tmp_priv->htcap) + is_htcapie = true; + } + + if (is_htcapie) { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE; + bes2600_info(BES2600_DBG_STA, "AP REMOVE HTCAP 11N %d\n",hw_priv->vif0_throttle); + } else { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE; + bes2600_info(BES2600_DBG_STA, "AP REMOVE 11BG %d\n",hw_priv->vif0_throttle); + } + bes2600_pwr_clear_busy_event(hw_priv, BES2600_JOIN_STATUS_AP); + break; + case BES2600_JOIN_STATUS_MONITOR: + bes2600_disable_listening(priv); + break; + default: + break; + } + /* TODO:COMBO: Change Queue Module */ + if (!__bes2600_flush(hw_priv, false, priv->if_id)) + wsm_unlock_tx(hw_priv); + + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->set_cts_work); + cancel_delayed_work_sync(&priv->pending_offchanneltx_work); + + del_timer_sync(&priv->mcast_timeout); + /* TODO:COMBO: May be reset of these variables "delayed_link_loss and + * join_status to default can be removed as dev_priv will be freed by + * mac80211 */ + priv->delayed_link_loss = 0; + priv->join_status = BES2600_JOIN_STATUS_PASSIVE; + wsm_unlock_tx(hw_priv); + + if ((priv->if_id ==1) && (priv->mode == NL80211_IFTYPE_AP + || priv->mode == NL80211_IFTYPE_P2P_GO)) { + hw_priv->is_go_thru_go_neg = false; + } + spin_lock(&hw_priv->vif_list_lock); + spin_lock(&priv->vif_lock); + hw_priv->vif_list[priv->if_id] = NULL; + hw_priv->if_id_slot &= (~BIT(priv->if_id)); + atomic_dec(&hw_priv->num_vifs); + if (atomic_read(&hw_priv->num_vifs) == 0) { + bes2600_free_keys(hw_priv); + memset(hw_priv->mac_addr, 0, ETH_ALEN); + } + spin_unlock(&priv->vif_lock); + spin_unlock(&hw_priv->vif_list_lock); + priv->listening = false; + + bes2600_debug_release_priv(priv); + + bes2600_tx_queues_unlock(hw_priv); + up(&hw_priv->conf_lock); + + if (atomic_read(&hw_priv->num_vifs) == 0) + flush_workqueue(hw_priv->workqueue); + memset(priv, 0, sizeof(struct bes2600_vif)); + up(&hw_priv->scan.lock); +} + +int bes2600_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p) +{ + int ret = 0; + bes2600_info(BES2600_DBG_STA, " !!! %s: type %d (%d), p2p %d (%d)\n", + __func__, new_type, vif->type, p2p, vif->p2p); + if (new_type != vif->type || vif->p2p != p2p) { + bes2600_remove_interface(dev, vif); + vif->type = new_type; + vif->p2p = p2p; + ret = bes2600_add_interface(dev, vif); + } + + return ret; +} + +int bes2600_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct bes2600_common *hw_priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; +#ifdef CONFIG_BES2600_TESTMODE + int max_power_level = 0; + int min_power_level = 0; +#endif + /* TODO:COMBO: adjust to multi vif interface + * IEEE80211_CONF_CHANGE_IDLE is still handled per bes2600_vif*/ + int if_id = 0; + struct bes2600_vif *priv; + + bes2600_info(BES2600_DBG_STA, "CONFIG CHANGED: %08x\n", changed); + + if (changed & + (IEEE80211_CONF_CHANGE_MONITOR|IEEE80211_CONF_CHANGE_IDLE)) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! */ + bes2600_info(BES2600_DBG_STA, + "ignore IEEE80211_CONF_CHANGE_MONITOR (%d)" + "IEEE80211_CONF_CHANGE_IDLE (%d)\n", + (changed & IEEE80211_CONF_CHANGE_MONITOR) ? 1 : 0, + (changed & IEEE80211_CONF_CHANGE_IDLE) ? 1 : 0); + return ret; + } + + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + /* TODO:COMBO:Change when support is available mac80211*/ + if (changed & IEEE80211_CONF_CHANGE_POWER) { + /*hw_priv->output_power = conf->power_level;*/ + bes2600_info(BES2600_DBG_STA, "Output power ++%d\n",conf->power_level); + hw_priv->output_power = 20; + bes2600_info(BES2600_DBG_STA, "Output power --%d\n",hw_priv->output_power); +#ifdef CONFIG_BES2600_TESTMODE + /* Testing if Power Level to set is out of device power range */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if (conf->chandef.chan->band == NL80211_BAND_2GHZ) { +#else + if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { +#endif + max_power_level = hw_priv->txPowerRange[0].max_power_level; + min_power_level = hw_priv->txPowerRange[0].min_power_level; + } else { + max_power_level = hw_priv->txPowerRange[1].max_power_level; + min_power_level = hw_priv->txPowerRange[1].min_power_level; + } + if (hw_priv->output_power > max_power_level) + hw_priv->output_power = max_power_level; + else if (hw_priv->output_power < min_power_level) + hw_priv->output_power = min_power_level; +#endif /* CONFIG_BES2600_TESTMODE */ + bes2600_info(BES2600_DBG_STA, "[STA] TX power: %d\n", + hw_priv->output_power); + WARN_ON(wsm_set_output_power(hw_priv, + hw_priv->output_power * 10, + if_id)); + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + /* Switch Channel commented for CC Mode */ + struct ieee80211_channel *ch = conf->chandef.chan; + + bes2600_info(BES2600_DBG_STA, "[STA] Freq %d (wsm ch: %d, type: %d).\n", + ch->center_freq, ch->hw_value, + cfg80211_get_chandef_type(&conf->chandef)); + /* Earlier there was a call to __bes2600_flush(). + Removed as deemed unnecessary */ + + hw_priv->channel = ch; + + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + bes2600_info(BES2600_DBG_STA, "[STA] Retry limits: %d (long), %d (short).\n", + conf->long_frame_max_tx_count, + conf->short_frame_max_tx_count); + spin_lock_bh(&hw_priv->tx_policy_cache.lock); + hw_priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; + hw_priv->short_frame_max_tx_count = + (conf->short_frame_max_tx_count < 0x0F) ? + conf->short_frame_max_tx_count : 0x0F; + hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count; + spin_unlock_bh(&hw_priv->tx_policy_cache.lock); + } + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + + return ret; +} + +void bes2600_update_filtering(struct bes2600_vif *priv) +{ + int ret; + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + bool bssid_filtering = !priv->rx_filter.bssid; + static struct wsm_beacon_filter_control bf_disabled = { + .enabled = 0, + .bcn_count = 1, + }; + bool ap_mode = 0; + static struct wsm_beacon_filter_table bf_table_auto = { + .numOfIEs = __cpu_to_le32(2), + .entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC, + .entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[0].oui[0] = 0x50, + .entry[0].oui[1] = 0x6F, + .entry[0].oui[2] = 0x9A, + + .entry[1].ieId = WLAN_EID_HT_OPERATION, + .entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + }; + static struct wsm_beacon_filter_control bf_auto = { + .enabled = WSM_BEACON_FILTER_ENABLE | + WSM_BEACON_FILTER_AUTO_ERP, + .bcn_count = 1, + }; + bf_auto.bcn_count = priv->bf_control.bcn_count; + + if (priv->join_status == BES2600_JOIN_STATUS_PASSIVE) + return; + else if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) + bssid_filtering = false; + + if (priv->vif && (priv->vif->type == NL80211_IFTYPE_AP)) + ap_mode = true; + /* + * When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ + if (priv->vif && priv->vif->p2p) + bssid_filtering = false; + + ret = wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); + if (!ret && !ap_mode) { + if (priv->vif) { + if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type) + ret = wsm_set_beacon_filter_table(hw_priv, &priv->bf_table, + priv->if_id); + else + ret = wsm_set_beacon_filter_table(hw_priv, &bf_table_auto, + priv->if_id); + } else + WARN_ON(1); + } + if (!ret && !ap_mode) { + if (priv->disable_beacon_filter) + ret = wsm_beacon_filter_control(hw_priv, + &bf_disabled, priv->if_id); + else { + if (priv->vif) { + if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type) + ret = wsm_beacon_filter_control(hw_priv, + &priv->bf_control, priv->if_id); + else + ret = wsm_beacon_filter_control(hw_priv, + &bf_auto, priv->if_id); + } else + WARN_ON(1); + } + } + + if (!ret) + ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, + priv->if_id); + + if (!ret) { + ret = wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, + priv->if_id); + } + + if (ret) + wiphy_err(priv->hw->wiphy, + "%s: Update filtering failed: %d.\n", + __func__, ret); + return; +} + +void bes2600_update_filtering_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, + update_filtering_work); + + bes2600_update_filtering(priv); +} + +void bes2600_set_beacon_wakeup_period_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, + set_beacon_wakeup_period_work); + + WARN_ON(wsm_set_beacon_wakeup_period(priv->hw_priv, + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0, priv->if_id)); +} + +u64 bes2600_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + static u8 broadcast_ipv6[ETH_ALEN] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 + }; + static u8 broadcast_ipv4[ETH_ALEN] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 + }; + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0); + struct netdev_hw_addr *ha; + int count = 0; + + if(priv == NULL){ + bes2600_info(BES2600_DBG_STA, "wlan0 removed before p2p-device\n"); + return netdev_hw_addr_list_count(mc_list); + } + + /* Disable multicast filtering */ + priv->has_multicast_subscription = false; + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + bes2600_info(BES2600_DBG_STA, "[STA] multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macAddress[count], + ha->addr, ETH_ALEN); + if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && + memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) + priv->has_multicast_subscription = true; + count++; + } + + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.numOfAddresses = __cpu_to_le32(count); + } + + return netdev_hw_addr_list_count(mc_list); +} + +void bes2600_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0); +#if 0 + bool listening = !!(*total_flags & + (FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); +#endif + *total_flags &= FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; + + if(priv != NULL){ + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + + priv->rx_filter.promiscuous = 0; + priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->bf_control.bcn_count = (*total_flags & + (FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ)) ? 1 : 0; +#if 0 + if (priv->listening ^ listening) { + priv->listening = listening; + wsm_lock_tx(hw_priv); + bes2600_update_listening(priv, listening); + wsm_unlock_tx(hw_priv); + } +#endif + bes2600_update_filtering(priv); + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + } +} + +int bes2600_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsdFlags; + + if (WARN_ON(!priv)) + return -EOPNOTSUPP; + +#ifdef P2P_MULTIVIF + if (priv->if_id == CW12XX_GENERIC_IF_ID) + return 0; +#endif + down(&hw_priv->conf_lock); + + if (queue < dev->queues) { + old_uapsdFlags = priv->uapsd_info.uapsdFlags; + + WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); + ret = wsm_set_tx_queue_params(hw_priv, + &priv->tx_queue_params.params[queue], + queue, priv->if_id); + if (ret) { + ret = -EINVAL; + goto out; + } + + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, params->txop, 0xc8, + params->uapsd); + ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); + if (ret) { + ret = -EINVAL; + goto out; + } + + if (priv->mode == NL80211_IFTYPE_STATION) { + ret = bes2600_set_uapsd_param(priv, &priv->edca); + #if 0 + if (!ret && priv->setbssparams_done && + (priv->join_status == BES2600_JOIN_STATUS_STA) && + (old_uapsdFlags != priv->uapsd_info.uapsdFlags)) + bes2600_set_pm(priv, &priv->powersave_mode); + #endif + } + } else + ret = -EINVAL; + +out: + up(&hw_priv->conf_lock); + + return ret; +} + +int bes2600_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct bes2600_common *hw_priv = dev->priv; + + memcpy(stats, &hw_priv->stats, sizeof(*stats)); + return 0; +} + +/* +int bes2600_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + int i; + struct bes2600_common *priv = dev->priv; + + for (i = 0; i < dev->queues; ++i) + bes2600_queue_get_stats(&priv->tx_queue[i], &stats[i]); + + return 0; +} +*/ + +int bes2600_set_pm(struct bes2600_vif *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + + if (priv->uapsd_info.uapsdFlags != 0) + pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG; + + if (memcmp(&pm, &priv->firmware_ps_mode, + sizeof(struct wsm_set_pm))) { + priv->firmware_ps_mode = pm; + return wsm_set_pm(priv->hw_priv, &pm, + priv->if_id); + } else { + return 0; + } +} + +int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct wsm_protected_mgmt_policy mgmt_policy; + +#ifdef P2P_MULTIVIF + WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); +#endif + memset(&mgmt_policy, 0, sizeof(mgmt_policy)); + down(&hw_priv->conf_lock); + bes2600_info(BES2600_DBG_STA, "%s, cmd:%d cipher:0x%08x idx:%d\n", + __func__, cmd, key->cipher, key->keyidx); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? + 1 : 0; + int idx = bes2600_alloc_key(hw_priv); + struct wsm_add_key *wsm_key = &hw_priv->keys[idx]; + + if (idx < 0) { + ret = -EINVAL; + goto finally; + } + + BUG_ON(pairwise && !sta); + if (sta) + peer_addr = sta->addr; + + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; + + /* only need to update cipher type of pairwise key */ + if (pairwise) + priv->cipherType = key->cipher; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + bes2600_free_key(hw_priv, idx); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wepPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wepPairwiseKey.keyData, + &key->key[0], key->keylen); + wsm_key->wepPairwiseKey.keyLength = key->keylen; + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wepGroupKey.keyData, + &key->key[0], key->keylen); + wsm_key->wepGroupKey.keyLength = key->keylen; + wsm_key->wepGroupKey.keyId = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_TKIP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkipPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->tkipPairwiseKey.tkipKeyData, + &key->key[0], 16); + memcpy(wsm_key->tkipPairwiseKey.txMicKey, + &key->key[16], 8); + memcpy(wsm_key->tkipPairwiseKey.rxMicKey, + &key->key[24], 8); + } else { + size_t mic_offset = + (priv->mode == NL80211_IFTYPE_AP) ? + 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkipGroupKey.tkipKeyData, + &key->key[0], 16); + memcpy(wsm_key->tkipGroupKey.rxMicKey, + &key->key[mic_offset], 8); + + /* TODO: Where can I find TKIP SEQ? */ + memset(wsm_key->tkipGroupKey.rxSeqCounter, + 0, 8); + wsm_key->tkipGroupKey.keyId = key->keyidx; + + } + break; + case WLAN_CIPHER_SUITE_CCMP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aesPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->aesPairwiseKey.aesKeyData, + &key->key[0], 16); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aesGroupKey.aesKeyData, + &key->key[0], 16); + /* TODO: Where can I find AES SEQ? */ + memset(wsm_key->aesGroupKey.rxSeqCounter, + 0, 8); + wsm_key->aesGroupKey.keyId = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + wsm_key->type = WSM_KEY_TYPE_IGTK_GROUP; + memcpy(wsm_key->igtkGroupKey.IGTKKeyData, &key->key[0], 16); + memset(wsm_key->igtkGroupKey.IPN, 0, 8); + wsm_key->igtkGroupKey.keyId = key->keyidx; + mgmt_policy.protectedMgmtEnable = 1; + wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, priv->if_id); + break; +#ifdef CONFIG_BES2600_WAPI_SUPPORT + case WLAN_CIPHER_SUITE_SMS4: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapiPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wapiPairwiseKey.wapiKeyData, + &key->key[0], 16); + memcpy(wsm_key->wapiPairwiseKey.micKeyData, + &key->key[16], 16); + wsm_key->wapiPairwiseKey.keyId = key->keyidx; + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapiGroupKey.wapiKeyData, + &key->key[0], 16); + memcpy(wsm_key->wapiGroupKey.micKeyData, + &key->key[16], 16); + wsm_key->wapiGroupKey.keyId = key->keyidx; + } + break; +#endif /* CONFIG_BES2600_WAPI_SUPPORT */ + default: + WARN_ON(1); + bes2600_free_key(hw_priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + +#ifdef CONFIG_BES2600_WAPI_SUPPORT + /* workaround: beacuse bes2600 can't install two unicast keys at the same time, + * remove last unicast key when install new unicast key. + */ + if(key->cipher == WLAN_CIPHER_SUITE_SMS4 && + pairwise && hw_priv->last_ins_wapi_usk_id != -1) { + struct wsm_remove_key last_wsm_key = { + .entryIndex = hw_priv->last_ins_wapi_usk_id, + }; + bes2600_free_key(hw_priv, last_wsm_key.entryIndex); + wsm_remove_key(hw_priv, &last_wsm_key, priv->if_id); + hw_priv->last_del_wapi_usk_id = hw_priv->last_ins_wapi_usk_id; + } +#endif + + ret = WARN_ON(wsm_add_key(hw_priv, wsm_key, priv->if_id)); + if (!ret) { + key->hw_key_idx = idx; +#ifdef CONFIG_BES2600_WAPI_SUPPORT + if(wsm_key->type == WSM_KEY_TYPE_WAPI_PAIRWISE) + hw_priv->last_ins_wapi_usk_id = idx; +#endif + } else { + bes2600_free_key(hw_priv, idx); + } + + if (!ret && (pairwise + || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) + && (priv->filter4.enable & 0x2)) + bes2600_set_arpreply(dev, vif); +#ifdef IPV6_FILTERING + if (!ret && (pairwise + || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) + && (priv->filter6.enable & 0x2)) + bes2600_set_na(dev, vif); +#endif /*IPV6_FILTERING*/ + + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .entryIndex = key->hw_key_idx, + }; + + if (wsm_key.entryIndex > WSM_KEY_MAX_IDX) { + ret = -EINVAL; + goto finally; + } + +#ifdef CONFIG_BES2600_WAPI_SUPPORT + /* workaround: beacuse bes2600 can't install two unicast keys at the same time, + * remove last unicast key when install new unicast key. + */ + if(hw_priv->last_del_wapi_usk_id == key->hw_key_idx) { + hw_priv->last_del_wapi_usk_id = -1; + ret = 0; + goto finally; + } else if (hw_priv->last_ins_wapi_usk_id == key->hw_key_idx) { + hw_priv->last_ins_wapi_usk_id = -1; + } +#endif + + bes2600_free_key(hw_priv, wsm_key.entryIndex); + ret = wsm_remove_key(hw_priv, &wsm_key, priv->if_id); + } else { + BUG_ON("Unsupported command"); + } + +finally: + up(&hw_priv->conf_lock); + return ret; +} + +void bes2600_wep_key_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif , wep_key_work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id); + struct bes2600_queue *queue = &hw_priv->tx_queue[queueId]; + __le32 wep_default_key_id = __cpu_to_le32( + priv->wep_default_key_id); + + BUG_ON(queueId >= 4); + + bes2600_dbg(BES2600_DBG_STA, "[STA] Setting default WEP key: %d\n", + priv->wep_default_key_id); + wsm_flush_tx(hw_priv); + WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id), priv->if_id)); +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_requeue(hw_priv, queue, + hw_priv->pending_frame_id, true); +#else + bes2600_queue_requeue(queue, hw_priv->pending_frame_id, true); +#endif + wsm_unlock_tx(hw_priv); +} + +int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct bes2600_common *hw_priv = hw->priv; + int ret; + __le32 val32; + bes2600_dbg(BES2600_DBG_TXRX_OPT,"set RTS threshold = %d\n\r", hw_priv->rtsvalue); + + spin_lock(&hw_priv->rtsvalue_lock); + if (hw_priv->rtsvalue != value) { + hw_priv->rtsvalue = value; + //bes2600_dbg(BES2600_DBG_TXRX_OPT,"set RTS value = %d\n\r", hw_priv->rtsvalue); + } else { + spin_unlock(&hw_priv->rtsvalue_lock); + return 0; + } + spin_unlock(&hw_priv->rtsvalue_lock); + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + /* mutex_lock(&priv->conf_mutex); */ + ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32), 0)); + /* mutex_unlock(&priv->conf_mutex); */ + + return ret; +} + +/* TODO: COMBO: Flush only a particular interface specific parts */ +int __bes2600_flush(struct bes2600_common *hw_priv, bool drop, int if_id) +{ + int i, ret; + struct bes2600_vif *priv = + __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id); + + /* clear tx queue directly if there is a bus error */ + if(hw_priv->bh_error) + drop = true; + + for (;;) { + /* TODO: correct flush handling is required when dev_stop. + * Temporary workaround: 2s + */ + if (drop) { + for (i = 0; i < 4; ++i) + bes2600_queue_clear(&hw_priv->tx_queue[i], + if_id); + } else { + ret = wait_event_timeout( + hw_priv->tx_queue_stats.wait_link_id_empty, + bes2600_queue_stats_is_empty( + &hw_priv->tx_queue_stats, -1, if_id), +#ifdef FPGA_SETUP + 10 * HZ); +#else + 2 * HZ); +#endif + } + + if (!drop && unlikely(ret <= 0)) { + bes2600_err(BES2600_DBG_STA, "__bes2600_flush: ETIMEDOUT.....\n"); + ret = -ETIMEDOUT; + break; + } else { + ret = 0; + } + + wsm_vif_lock_tx(priv); + if (unlikely(!bes2600_queue_stats_is_empty( + &hw_priv->tx_queue_stats, -1, if_id))) { + /* Highly unlekely: WSM requeued frames. */ + wsm_unlock_tx(hw_priv); + continue; + } + break; + } + return ret; +} + +void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct bes2600_vif *priv = NULL; + struct bes2600_common *hw_priv = hw->priv; + unsigned int ret = 0; + int i; + + /* get queue status */ + if (vif) { + priv = cw12xx_get_vif_from_ieee80211(vif); + ret |= !bes2600_queue_stats_is_empty( + &hw_priv->tx_queue_stats, -1, priv->if_id); + } else { + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if (!(hw_priv->if_id_slot & BIT(priv->if_id))) + return; + ret |= !bes2600_queue_stats_is_empty( + &hw_priv->tx_queue_stats, -1, priv->if_id); + } + } + + /* no need to do the next work if queue was already clear */ + if(ret == 0) { + bes2600_info(BES2600_DBG_STA, "no need to flush\n"); + return; + } + + /* do flush operation */ + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_FLUSH); + if (vif) { + priv = cw12xx_get_vif_from_ieee80211(vif); + if (!(hw_priv->if_id_slot & BIT(priv->if_id))) + return; + if (!WARN_ON(__bes2600_flush(hw_priv, drop, priv->if_id))) + wsm_unlock_tx(hw_priv); + } else { + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if (!(hw_priv->if_id_slot & BIT(priv->if_id))) + return; + if (!WARN_ON(__bes2600_flush(hw_priv, drop, priv->if_id))) + wsm_unlock_tx(hw_priv); + } + } + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_FLUSH); + + return; +} + +int bes2600_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + int ret; + struct bes2600_common *hw_priv = hw->priv; + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + int if_id = priv->if_id; + + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC); + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + + + bes2600_info(BES2600_DBG_ROC, "ROC IN %d ch %d\n", priv->if_id, chan->hw_value); + hw_priv->roc_if_id = priv->if_id; + ret = WARN_ON(__bes2600_flush(hw_priv, false, if_id)); + wsm_unlock_tx(hw_priv); + ret = bes2600_enable_listening(priv, chan); + + if (!ret) { + atomic_set(&hw_priv->remain_on_channel, 1); + queue_delayed_work(hw_priv->workqueue, + &hw_priv->rem_chan_timeout, + duration * HZ / 1000); + priv->join_status = BES2600_JOIN_STATUS_MONITOR; + ieee80211_ready_on_channel(hw); + } else { + hw_priv->roc_if_id = -1; + up(&hw_priv->scan.lock); + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC); + } + + bes2600_info(BES2600_DBG_ROC, "ROC OUT %d\n", priv->if_id); + + /* set the channel to supplied ieee80211_channel pointer, if it + is not set. This is to remove the crash while sending a probe res + in listen state. Later channel will updated on + IEEE80211_CONF_CHANGE_CHANNEL event*/ + if(!hw_priv->channel) { + hw_priv->channel = chan; + } + //hw_priv->roc_cookie = cookie; + up(&hw_priv->conf_lock); + return ret; +} + +int bes2600_cancel_remain_on_channel(struct ieee80211_hw *hw +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + , struct ieee80211_vif *vif +#endif + ) +{ + struct bes2600_common *hw_priv = hw->priv; + + bes2600_info(BES2600_DBG_STA, "[STA] Cancel remain on channel\n"); + + if (atomic_read(&hw_priv->remain_on_channel)) + cancel_delayed_work_sync(&hw_priv->rem_chan_timeout); + + if (atomic_read(&hw_priv->remain_on_channel)) + bes2600_rem_chan_timeout(&hw_priv->rem_chan_timeout.work); + + return 0; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +void bes2600_channel_switch_cb(struct bes2600_common *hw_priv) +{ + //wsm_unlock_tx(hw_priv); /* already unlock in wsm_channel_switch_indication */ +} + +void bes2600_free_event_queue(struct bes2600_common *hw_priv) +{ + LIST_HEAD(list); + + spin_lock(&hw_priv->event_queue_lock); + list_splice_init(&hw_priv->event_queue, &list); + spin_unlock(&hw_priv->event_queue_lock); + + __bes2600_free_event_queue(&list); +} + +void bes2600_event_handler(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, event_handler); + struct bes2600_vif *priv; + struct bes2600_wsm_event *event; + LIST_HEAD(list); + + spin_lock(&hw_priv->event_queue_lock); + list_splice_init(&hw_priv->event_queue, &list); + spin_unlock(&hw_priv->event_queue_lock); + + down(&hw_priv->conf_lock); + list_for_each_entry(event, &list, link) { + priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, event->if_id); + if (!priv) { + bes2600_dbg(BES2600_DBG_STA, "[CQM] Event for non existing " + "interface, ignoring.\n"); + continue; + } + switch (event->evt.eventId) { + case WSM_EVENT_ERROR: + /* I even don't know what is it about.. */ + //STUB(); + break; + case WSM_EVENT_BSS_LOST: + { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status > BES2600_BSS_LOSS_NONE) { + spin_unlock(&priv->bss_loss_lock); + break; + } + priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING; + spin_unlock(&priv->bss_loss_lock); + bes2600_info(BES2600_DBG_STA, "[CQM] BSS lost.\n"); + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_BSS_LOST); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + if (!down_trylock(&hw_priv->scan.lock)) { + up(&hw_priv->scan.lock); + priv->delayed_link_loss = 0; + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, 0); + } else { + /* Scan is in progress. Delay reporting. */ + /* Scan complete will trigger bss_loss_work */ + priv->delayed_link_loss = 1; + /* Also we're starting watchdog. */ + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, 10 * HZ); + } + break; + } + case WSM_EVENT_BSS_REGAINED: + { + bes2600_info(BES2600_DBG_STA, "[CQM] BSS regained.\n"); + priv->delayed_link_loss = 0; + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = BES2600_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_BSS_LOST); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + break; + } + case WSM_EVENT_RADAR_DETECTED: + //STUB(); + break; + case WSM_EVENT_RCPI_RSSI: + { + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + int rcpi_rssi = (int)(event->evt.eventData & 0xFF); + int cqm_evt; + if (priv->cqm_use_rssi) + rcpi_rssi = (s8)rcpi_rssi; + else + rcpi_rssi = rcpi_rssi / 2 - 110; + + cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + bes2600_info(BES2600_DBG_STA, "[CQM] RSSI event: %d.\n", rcpi_rssi); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi, GFP_KERNEL); +#else + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, GFP_KERNEL); +#endif + break; + } + case WSM_EVENT_BT_INACTIVE: + //STUB(); + break; + case WSM_EVENT_BT_ACTIVE: + //STUB(); + break; + case WSM_EVENT_INACTIVITY: + { + int link_id = ffs((u32)(event->evt.eventData)) - 1; + struct sk_buff *skb; + struct ieee80211_mgmt *deauth; + struct bes2600_link_entry *entry = NULL; + + bes2600_info(BES2600_DBG_STA, "Inactivity Event Recieved for " + "link_id %d\n", link_id); + cw12xx_unmap_link(priv, link_id); + + skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); + skb_reserve(skb, 64); + deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); + WARN_ON(!deauth); + entry = &priv->link_id_db[link_id - 1]; + deauth->duration = 0; + memcpy(deauth->da, priv->vif->addr, ETH_ALEN); + memcpy(deauth->sa, entry->mac/*priv->link_id_db[i].mac*/, ETH_ALEN); + memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN); + deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS); + deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; + deauth->seq_ctrl = 0; + ieee80211_rx_irqsafe(priv->hw, skb); + bes2600_info(BES2600_DBG_STA, " Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); + queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); + break; + } + case WSM_EVENT_PS_MODE_ERROR: + { + bes2600_err(BES2600_DBG_STA, " PS Mode Error, Reason:%u\n", event->evt.eventData); + + if (event->evt.eventData != WSM_PS_ERROR_AP_NO_DATA_AFTER_TIM && + !priv->uapsd_info.uapsdFlags && + (priv->user_pm_mode != WSM_PSM_PS)) + { + bes2600_pwr_mark_ap_lp_bad(hw_priv); + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + } + break; + } + case WSM_EVENT_WAKEUP_EVENT: + bes2600_info(BES2600_DBG_STA, "wifi wakeup, Reason:%u\n", event->evt.eventData); + bes2600_chrdev_wifi_update_wakeup_reason(event->evt.eventData); + break; + } + } + up(&hw_priv->conf_lock); + __bes2600_free_event_queue(&list); +} + +void bes2600_bss_loss_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, bss_loss_work.work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + int timeout; /* in beacons */ +#ifdef BSS_LOSS_CHECK + struct sk_buff *skb; + struct ieee80211_tx_info *info; + static int bl_ck_cnt = 0; + static int bl_cfm_cnt = 0; +#endif + + timeout = priv->cqm_link_loss_count - + priv->cqm_beacon_loss_count; + + /* Skip the confimration procedure in P2P case */ + if (priv->vif->p2p) + goto report; + + spin_lock(&priv->bss_loss_lock); +#ifdef BSS_LOSS_CHECK + if (!priv->vif->bss_conf.assoc) { + priv->bss_loss_status = BES2600_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + bl_ck_cnt = 0; + bl_cfm_cnt = 0; + return; + } +#endif + if (priv->bss_loss_status == BES2600_BSS_LOSS_CHECKING) { +#ifdef BSS_LOSS_CHECK + spin_unlock(&priv->bss_loss_lock); + bes2600_info(BES2600_DBG_STA, "bl checking\n"); + priv->cmq_tx_success_count = 0; + bl_ck_cnt = 0; + bl_cfm_cnt = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); +#else + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); +#endif + if (!(WARN_ON(!skb))) { + info = IEEE80211_SKB_CB(skb); + info->control.vif = priv->vif; + bes2600_tx(priv->hw, NULL, skb); + /* Start watchdog -- if nullfunc TX doesn't fail + * in 1 sec, forward event to upper layers */ + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, 1 * HZ); + } + return; +#else + priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMED; +#endif + } else if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMING) { +#ifdef BSS_LOSS_CHECK + /* reset cca to workaround rx stuck issue */ + // bes2600_cca_soft_reset(); + + /* succeeded to send last null frame */ + bl_cfm_cnt = 0; + /* keep checking to wait bss regain*/ + bes2600_info(BES2600_DBG_STA, "bl ck %d\n", bl_ck_cnt); + if (bl_ck_cnt++ < BSS_LOSS_CK_THR) { + spin_unlock(&priv->bss_loss_lock); + priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); +#else + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); +#endif + if (!(WARN_ON(!skb))) { + info = IEEE80211_SKB_CB(skb); + info->control.vif = priv->vif; + bes2600_tx(priv->hw, NULL, skb); + /* detect every 3s until receive bss regain event */ + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, BSS_LOSS_CK_INV * HZ / 1000); + } + return; + } else { + bl_ck_cnt = 0; + } +#else + priv->bss_loss_status = BES2600_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + return; +#endif + } else if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMED) { +#ifdef BSS_LOSS_CHECK + /* failed to send last null frame */ + /* check if continous failures occur */ + if (priv->cmq_tx_success_count != 0) { + bes2600_info(BES2600_DBG_STA, "bl reset ck cfm s_cnt=%d\n", priv->cmq_tx_success_count); + bl_ck_cnt = 0; + bl_cfm_cnt = 0; + priv->cmq_tx_success_count = 0; + } + bes2600_info(BES2600_DBG_STA, "bl cfm %d\n", bl_cfm_cnt); + if (bl_cfm_cnt++ < BSS_LOSS_CFM_THR) { + spin_unlock(&priv->bss_loss_lock); + priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false); +#else + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); +#endif + if (!(WARN_ON(!skb))) { + info = IEEE80211_SKB_CB(skb); + info->control.vif = priv->vif; + bes2600_tx(priv->hw, NULL, skb); + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, BSS_LOSS_CK_INV * HZ / 1000); + } + return; + } else { + bl_cfm_cnt = 0; + } +#else + priv->bss_loss_status = BES2600_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + return; +#endif + } + spin_unlock(&priv->bss_loss_lock); + +report: + if (priv->cqm_beacon_loss_count) { + bes2600_info(BES2600_DBG_STA, "[CQM] Beacon loss.\n"); + if (timeout <= 0) + timeout = 0; +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL); +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + } else { + timeout = 0; + } + + cancel_delayed_work_sync(&priv->connection_loss_work); + queue_delayed_work(hw_priv->workqueue, + &priv->connection_loss_work, + timeout * HZ / 10); + + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = BES2600_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); +} + +void bes2600_connection_loss_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, + connection_loss_work.work); + bes2600_info(BES2600_DBG_STA, "[CQM] Reporting connection loss.\n"); + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST); + ieee80211_connection_loss(priv->vif); +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + // set disconnected in BSS_CHANGED_ASSOC + // bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTED); +#endif +} + +void bes2600_tx_failure_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, tx_failure_work); + bes2600_info(BES2600_DBG_STA, "[CQM] Reporting TX failure.\n"); +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL); +#else /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + (void)priv; +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ +} + +#ifdef CONFIG_BES2600_TESTMODE +/** + * bes2600_device_power_calc- Device power calculation + * from values fetch from SDD File. + * + * @priv: the private structure + * @Max_output_power: Power fetch from SDD + * @fe_cor: front-end loss correction + * @band: Either 2GHz or 5GHz + * + */ +void bes2600_device_power_calc(struct bes2600_common *hw_priv, + s16 max_output_power, s16 fe_cor, u32 band) +{ + s16 power_calc; + + power_calc = max_output_power - fe_cor; + if ((power_calc % 16) != 0) + power_calc += 16; + + hw_priv->txPowerRange[band].max_power_level = power_calc/16; + /* + * 12dBm is control range supported by firmware. + * This means absolute min power is + * max_power_level - 12. + */ + hw_priv->txPowerRange[band].min_power_level = + hw_priv->txPowerRange[band].max_power_level - 12; + hw_priv->txPowerRange[band].stepping = 1; + +} +#endif +/* ******************************************************************** */ +/* Internal API */ + + + +/* +* This function is called to Parse the SDD file + *to extract listen_interval and PTA related information +*/ +int bes2600_parse_SDD_file(struct bes2600_common *hw_priv) +{ + u8 *sdd_data = (u8 *)hw_priv->sdd->data; +#ifdef CONFIG_BES2600_TESTMODE + s16 max_output_power_2G = 0; + s16 max_output_power_5G = 0; + s16 fe_cor_2G = 0; + s16 fe_cor_5G = 0; + int i; +#endif + struct bes2600_sdd { + u8 id ; + u8 length ; + u8 data[] ; + } *pElement; + int parsedLength = 0; + #define SDD_PTA_CFG_ELT_ID 0xEB +#ifdef CONFIG_BES2600_TESTMODE + #define SDD_MAX_OUTPUT_POWER_2G4_ELT_ID 0xE3 + #define SDD_MAX_OUTPUT_POWER_5G_ELT_ID 0xE4 + #define SDD_FE_COR_2G4_ELT_ID 0x30 + #define SDD_FE_COR_5G_ELT_ID 0x31 + #define MIN(x, y, z) (x < y ? (x < z ? x : z) : (y < z ? y : z)) +#endif + #define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0) + + hw_priv->is_BT_Present = false; + + pElement = (struct bes2600_sdd *)sdd_data; + + pElement = (struct bes2600_sdd *)((u8 *)pElement + + FIELD_OFFSET(struct bes2600_sdd, data) + pElement->length); + + parsedLength += (FIELD_OFFSET(struct bes2600_sdd, data) + + pElement->length); + + while (parsedLength <= hw_priv->sdd->size) { + switch (pElement->id) { + case SDD_PTA_CFG_ELT_ID: + { + hw_priv->conf_listen_interval = + (*((u16 *)pElement->data+1) >> 7) & 0x1F; + hw_priv->is_BT_Present = true; + bes2600_dbg(BES2600_DBG_STA, "PTA element found.\n"); + bes2600_dbg(BES2600_DBG_STA, "Listen Interval %d\n", + hw_priv->conf_listen_interval); + } + break; +#ifdef CONFIG_BES2600_TESTMODE + case SDD_MAX_OUTPUT_POWER_2G4_ELT_ID: + { + max_output_power_2G = + *((s16 *)pElement->data); + } + break; + case SDD_FE_COR_2G4_ELT_ID: + { + fe_cor_2G = + *((s16 *)pElement->data); + } + break; + case SDD_MAX_OUTPUT_POWER_5G_ELT_ID: + { + max_output_power_5G = + *((s16 *)(pElement->data + 4)); + } + break; + case SDD_FE_COR_5G_ELT_ID: + { + fe_cor_5G = MIN( + *((s16 *)pElement->data), + *((s16 *)(pElement->data + 2)), + *((s16 *)(pElement->data + 4))); + + fe_cor_5G = MIN( + fe_cor_5G, + *((s16 *)(pElement->data + 6)), + *((s16 *)(pElement->data + 8))); + } + break; +#endif + + default: + break; + } + + pElement = (struct bes2600_sdd *) + ((u8 *)pElement + FIELD_OFFSET(struct bes2600_sdd, data) + + pElement->length); + parsedLength += + (FIELD_OFFSET(struct bes2600_sdd, data) + pElement->length); + } + + if (hw_priv->is_BT_Present == false) { + bes2600_dbg(BES2600_DBG_STA, "PTA element NOT found.\n"); + hw_priv->conf_listen_interval = 0; + } +#ifdef CONFIG_BES2600_TESTMODE + /* Max/Min Power Calculation for 2.4G */ + bes2600_device_power_calc(hw_priv, max_output_power_2G, + fe_cor_2G, NL80211_BAND_2GHZ); + /* Max/Min Power Calculation for 5G */ + bes2600_device_power_calc(hw_priv, max_output_power_5G, + fe_cor_5G, NL80211_BAND_5GHZ); + + for (i = 0; i < 2; ++i) { + bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] Power Values Read from SDD %s:" + "min_power_level[%d]: %d max_power_level[%d]:" + "%d stepping[%d]: %d\n", __func__, i, + hw_priv->txPowerRange[i].min_power_level, i, + hw_priv->txPowerRange[i].max_power_level, i, + hw_priv->txPowerRange[i].stepping); + } + + + bes2600_dbg(BES2600_DBG_TEST_MODE, "%s output power before %d\n",__func__,hw_priv->output_power); + /*BUG:TX output power is not set untill config_bes2600 is called*/ + /*This would lead to 0 power set in fw and would effect scan & p2p-find*/ + /*Setting to default value here from sdd which would be overwritten when*/ + /*we make connection to AP.This value is used only during scan & p2p-ops*/ + /*untill AP connection is made*/ + if (!hw_priv->output_power) + hw_priv->output_power=hw_priv->txPowerRange[NL80211_BAND_2GHZ].max_power_level; + + bes2600_dbg(BES2600_DBG_TEST_MODE, "%s output power after %d\n",__func__,hw_priv->output_power); +#else + bes2600_dbg(BES2600_DBG_STA, "%s output power before %d\n",__func__,hw_priv->output_power); + /*BUG:TX output power: Hardcoding to 20dbm if CCX is not enabled*/ + /*TODO: This might change*/ + if (!hw_priv->output_power) + hw_priv->output_power=20; + bes2600_dbg(BES2600_DBG_STA, "%s output power after %d\n",__func__,hw_priv->output_power); +#endif + return 0; + + #undef SDD_PTA_CFG_ELT_ID + #undef FIELD_OFFSET +} + +#ifndef USE_FEM +#define USE_FEM 0 +#endif +const uint8_t sdd_22[] = +{ + 0xc0, 0x02, 0x02, 0x03, + 0xc1, 0x06, 0x00, 0x01, 0x00, 0x00, 0x4c, 0x00, + 0xc4, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0x02, 0x90, 0x65, + 0xc7, 0x02, 0x05, 0x00, + 0xe9, 0x02, 0xB8, 0x0B, // SddWakeupDriftInUsPerSecond=3000 + 0xea, 0x0a, 0x00, 0x00, 0x38, 0x00, 0x80, 0x00, 0x78, 0x00, 0x80, 0x00, + 0xeb, 0x1e, 0x06, 0x00, 0x81, 0x02, 0x01, 0x34, 0x0d, 0x21, 0x11, 0x1d, 0x7c, 0x85, 0x6b, 0x6e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x02, 0x00, 0x00, + 0xc1, 0x06, 0x01, 0x01, 0x00, 0x00, 0x30, 0x00, + 0xf0, 0x22, 0x41, 0x44, 0x42, 0x5f, 0x32, 0x39, 0x30, 0x35, 0x5f, 0x31, 0x32, 0x35, 0x30, 0x5f, 0x52, 0x32, 0x30, 0x5f, 0x4c, 0x6f, 0x77, 0x5f, 0x53, 0x4d, 0x50, 0x53, 0x5f, 0x76, 0x37, 0x2e, 0x30, 0x31, 0x00, 0x00, + 0xfe, 0x02, 0x00, 0x00, + 0xc1, 0x06, 0x03, 0x01, 0x00, 0x00, 0xb4, 0x02, +//2 TODO: we must make sure the function bes2600_parse_SDD_file's array & sdd values the same(0xec& 0xed) +#if TEST_11B //2020-2-4 + 0xec, 0x12, 0x05, 0x00, 0x01, 0x78, 0x78, 0x02, 0x78, 0x78, 0x0b, 0x78, 0x78, 0x0c, 0x78, 0x78, 0x0d, 0x78, 0x78, 0x00, +#else + 0xec, 0x12, 0x05, 0x00, 0x01, 0xc0, 0xc0, 0x02, 0xc0, 0xc0, 0x0b, 0xc0, 0xc0, 0x0c, 0xc0, 0xc0, 0x0d, 0xc0, 0xc0, 0x00, +#endif + 0xed, 0x0e, 0x05, 0x00, 0xb8, 0x78, 0x22, 0x78, 0x34, 0x78, 0x64, 0x78, 0x95, 0x78, 0x00, 0x00, + 0xe3, 0x1a, 0x1c, 0x01, 0x1c, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x10, 0x01, 0x00, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe4, 0x1a, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x14, 0x01, 0x04, 0x01, 0xf4, 0x00, 0xe4, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x1a, 0x8d, 0x03, 0x73, 0x02, 0x73, 0x03, 0x40, 0x03, 0xb3, 0x02, 0x40, 0x04, 0xcd, 0x04, 0x5f, 0x00, 0x00, 0x05, 0x5f, 0x00, 0x33, 0x05, 0x5f, 0x00, 0x00, 0x00, + 0x21, 0x1a, 0x73, 0x03, 0x73, 0x02, 0x8d, 0x02, 0x40, 0x03, 0x1a, 0x02, 0x40, 0x04, 0x00, 0x04, 0x38, 0x00, 0xcd, 0x04, 0x66, 0x00, 0x00, 0x05, 0x57, 0x00, 0x00, 0x00, + 0x22, 0x02, 0x07, 0x00, + 0x23, 0x02, 0x07, 0x00, + 0x30, 0x02, 0x08, 0x00, + 0x31, 0x02, 0x08, 0x00, + 0xe0, 0x02, 0x3a, 0x00, + 0xe1, 0x02, 0x32, 0x00, + 0x40, 0x0a, 0xf4, 0xff, 0x42, 0x00, 0xdd, 0xff, 0x3f, 0x00, 0x00, 0x00, + 0x41, 0x0a, 0x80, 0xff, 0x63, 0x00, 0xd8, 0xff, 0x20, 0x00, 0x00, 0x00, + 0x46, 0x22, 0x00, 0x00, 0x04, 0x1a, 0x08, 0x05, 0x01, 0x9f, 0x00, 0x00, 0x9a, 0xfd, 0x92, 0xff, 0x0a, 0x0a, 0x00, 0x00, 0xc0, 0x02, 0x6c, 0x00, 0x7c, 0xf5, 0x00, 0x00, 0x64, 0xfe, 0xca, 0xff, 0x78, 0x06, 0x00, 0x00, + 0x47, 0x22, 0x00, 0x00, 0xbe, 0xf6, 0xe4, 0x00, 0x10, 0x05, 0x00, 0x00, 0x27, 0x01, 0x5c, 0x01, 0x44, 0x00, 0x00, 0x00, 0x14, 0xfe, 0x93, 0xff, 0xb0, 0x00, 0x00, 0x00, 0x70, 0x01, 0x4a, 0x00, 0x6f, 0xff, 0x00, 0x00, + 0x44, 0x22, 0x00, 0x00, 0xb0, 0x03, 0x53, 0xfc, 0xf9, 0x05, 0x00, 0x00, 0xf6, 0xfe, 0x84, 0x00, 0x36, 0xff, 0x00, 0x00, 0x3b, 0x01, 0x68, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x78, 0xfe, 0x7d, 0x00, 0x51, 0xff, 0x00, 0x00, + 0x45, 0x22, 0x00, 0x00, 0xda, 0x03, 0xf0, 0xff, 0x34, 0xff, 0x00, 0x00, 0x9e, 0xff, 0xfd, 0xff, 0x0d, 0x00, 0x00, 0x00, 0xa8, 0x00, 0xe0, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x7e, 0xff, 0x29, 0x00, 0xf0, 0xff, 0x00, 0x00, + 0x42, 0x22, 0x00, 0x00, 0x08, 0xf8, 0xf0, 0xff, 0x58, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x89, 0xff, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd2, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, + 0x43, 0x22, 0x00, 0x00, 0xac, 0x00, 0x0a, 0xff, 0x63, 0x03, 0x00, 0x00, 0x31, 0xff, 0xf2, 0x00, 0xed, 0xfe, 0x00, 0x00, 0x58, 0x00, 0xa2, 0xff, 0x93, 0x00, 0x00, 0x00, 0xb4, 0xff, 0x65, 0x00, 0x74, 0xff, 0x00, 0x00, +#if DPD_CALI +#if ALI_CONFG + //2020-2-4 +#if TEST_11B + 0x48, 0x16, 0x20, 0x01, 0x20, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, +#else + 0x48, 0x16, 0x00, 0x02, 0x00, 0x02, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xa0, 0x01, 0xa0, 0x01, 0xa0, 0x01, +#endif +#else + 0x48, 0x16, 0x00, 0x01, 0x00, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, +#endif +#else + 0x48, 0x16, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, +#endif +#if (CHIP_WIFI_ROM_VER == 2) + 0x49, 0x16, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x4c, 0x01, 0x2d, 0x01, 0x10, 0x01, 0xf5, 0x00, 0xde, 0x00, +#else + 0x49, 0x16, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, +#endif + 0x50, 0x06, 0xe4, 0x48, 0x87, 0x43, 0xef, 0xe8, + 0x51, 0x06, 0x9b, 0x0c, 0x67, 0xef, 0x4f, 0x16, + 0x52, 0x1a, 0x00, 0x00, 0xb4, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x1a, 0x00, 0x00, 0x9a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x1a, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x1a, 0x00, 0x00, 0x10, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x1a, 0x00, 0x00, 0xf3, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x1a, 0x00, 0x00, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +#if WIFI_OUT_FEM + 0xc6, 0x22, 0, 0, 0, 8, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 16, 0, 16, 0, 0, 0, 8, 0, 16, 0, 16, 0, 0, +#else + +#ifdef CHIP_BEST2002 + //bes EVB sw gpio20 + 0xc6, 0x22, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, +#else //2003 +//gpio24 io number = 2*8+4=20 +#if (20 == RF_TX_CONTROL_IO ) + //0xc6 eco proprietary; bailu EVB sw gpio24 + 0xc6, 0x22, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, +#elif(12 == RF_TX_CONTROL_IO ) + //0xc6 eco proprietary; yunque EVB sw gpio14 or bes EVB sw gpio20 + 0xc6, 0x22, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, +#elif(2 == RF_TX_CONTROL_IO ) + 0xc6, 0x22, 0, 2, 0, 2, 0, 16, 0, 16, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, +#else + //0xc6 eco proprietary; yunque EVB sw gpio14 or bes EVB sw gpio20 + 0xc6, 0x22, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, +#endif +#endif + +#endif + + 0xfe, 0x02, 0x00, 0x00, + 0xc1, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +struct firmware g_sdd; + +int bes2600_setup_mac(struct bes2600_common *hw_priv) +{ + int ret = 0, if_id; + if (!hw_priv->sdd) { + const char *sdd_path = NULL; + struct wsm_configuration cfg = { + .dot11StationId = &hw_priv->mac_addr[0], + }; + + switch (hw_priv->hw_revision) { + case BES2600_HW_REV_CUT10: + sdd_path = SDD_FILE_10; + break; + case BES2600_HW_REV_CUT11: + sdd_path = SDD_FILE_11; + break; + case BES2600_HW_REV_CUT20: + sdd_path = SDD_FILE_20; + break; + case BES2600_HW_REV_CUT22: + sdd_path = SDD_FILE_22; + break; + case CW1250_HW_REV_CUT11: + sdd_path = SDD_FILE_1250_11; + break; +#if defined(BES2600_DETECTION_LOGIC) + case BES2600_HW_REV_CUT10: + sdd_path = SDD_FILE_1260_10; + break; +#endif/*BES2600_DETECTION_LOGIC*/ + default: + BUG_ON(1); + } + +#ifdef CONFIG_BES2600_STATIC_SDD + g_sdd.data = sdd_22; + g_sdd.size = sizeof(sdd_22); + hw_priv->sdd = &g_sdd; + cfg.dpdData = sdd_22; + cfg.dpdData_size = g_sdd.size; +#else + ret = request_firmware(&hw_priv->sdd, + sdd_path, hw_priv->pdev); + + if (unlikely(ret)) { + bes2600_dbg(BES2600_DBG_ERROR, + "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; + } + + cfg.dpdData = hw_priv->sdd->data; + cfg.dpdData_size = hw_priv->sdd->size; +#endif + + for (if_id = 0; if_id < 2; + if_id++) { + /* Set low-power mode. */ + ret |= WARN_ON(wsm_configuration(hw_priv, &cfg, + if_id)); + } + /* Parse SDD file for PTA element */ + //bes2600_parse_SDD_file(hw_priv); + } + + if (ret) + return ret; + + return 0; +} + +void bes2600_pending_offchanneltx_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, pending_offchanneltx_work.work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + down(&hw_priv->conf_lock); + + bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN PEND IN\n"); + bes2600_disable_listening(priv); + hw_priv->roc_if_id = -1; + bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN PEND OUT\n"); + + up(&hw_priv->scan.lock); + up(&hw_priv->conf_lock); +} + +void bes2600_offchannel_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, offchannel_work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id); + struct bes2600_queue *queue = &hw_priv->tx_queue[queueId]; + + BUG_ON(queueId >= 4); + BUG_ON(!hw_priv->channel); + + if (unlikely(down_trylock(&hw_priv->scan.lock))) { + int ret = 0; + bes2600_dbg(BES2600_DBG_STA, "bes2600_offchannel_work***** drop frame\n"); +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id); +#else + ret = bes2600_queue_remove(queue, hw_priv->pending_frame_id); +#endif + if (ret) + bes2600_err(BES2600_DBG_STA, "bes2600_offchannel_work: " + "queue_remove failed %d\n", ret); + wsm_unlock_tx(hw_priv); + return; + } + down(&hw_priv->conf_lock); + bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN WORK IN %d\n", priv->if_id); + hw_priv->roc_if_id = priv->if_id; + if (likely(!priv->join_status)) { + wsm_vif_flush_tx(priv); + bes2600_enable_listening(priv, hw_priv->channel); + /* bes2600_update_filtering(priv); */ + } + if (unlikely(!priv->join_status)) +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id); +#else + bes2600_queue_remove(queue, hw_priv->pending_frame_id); +#endif /*CONFIG_BES2600_TESTMODE*/ + else +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_requeue(hw_priv, queue, + hw_priv->pending_frame_id, false); +#else + bes2600_queue_requeue(queue, hw_priv->pending_frame_id, false); +#endif + + queue_delayed_work(hw_priv->workqueue, + &priv->pending_offchanneltx_work, 204 * HZ/1000); + bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN WORK OUT %d\n", priv->if_id); + up(&hw_priv->conf_lock); + wsm_unlock_tx(hw_priv); +} + +void bes2600_join_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, join_work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id); + struct bes2600_queue *queue = &hw_priv->tx_queue[queueId]; + const struct bes2600_txpriv *txpriv = NULL; + struct sk_buff *skb = NULL; + const struct wsm_tx *wsm; + const struct ieee80211_hdr *frame; + const u8 *bssid; + struct cfg80211_bss *bss; + const u8 *ssidie; + const u8 *dtimie; + const struct ieee80211_tim_ie *tim = NULL; + struct wsm_protected_mgmt_policy mgmt_policy; + struct ieee80211_conf *conf = &hw_priv->hw->conf; + struct wsm_template_frame probe_tmp = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + /*struct wsm_reset reset = { + .reset_statistics = true, + };*/ + + + BUG_ON(queueId >= 4); + if (bes2600_queue_get_skb(queue, hw_priv->pending_frame_id, + &skb, &txpriv)) { + wsm_unlock_tx(hw_priv); + return; + } + wsm = (struct wsm_tx *)&skb->data[0]; + frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; + bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ + + BUG_ON(!wsm); + BUG_ON(!hw_priv->channel); + + if (unlikely(priv->join_status)) { + atomic_set(&priv->connect_in_process, 0); + wsm_lock_tx(hw_priv); + bes2600_unjoin_work(&priv->unjoin_work); + } + + cancel_delayed_work_sync(&priv->join_timeout); + + bss = cfg80211_get_bss(hw_priv->hw->wiphy, hw_priv->channel, bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + + if (!bss) { +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, hw_priv->pending_frame_id); +#else + bes2600_queue_remove(queue, hw_priv->pending_frame_id); +#endif /*CONFIG_BES2600_TESTMODE*/ + wsm_unlock_tx(hw_priv); + return; + } + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + dtimie = ieee80211_bss_get_ie(bss, WLAN_EID_TIM); + if (dtimie) + tim = (struct ieee80211_tim_ie *)&dtimie[2]; + + down(&hw_priv->conf_lock); + { + struct wsm_switch_channel channel; + struct wsm_join join = { + .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .preambleType = WSM_JOIN_PREAMBLE_SHORT, + .probeForJoin = 1, + /* dtimPeriod will be updated after association */ + .dtimPeriod = 1, + .beaconInterval = bss->beacon_interval, + }; + + if (priv->if_id) + join.flags |= WSM_FLAG_MAC_INSTANCE_1; + else + join.flags &= ~WSM_FLAG_MAC_INSTANCE_1; + + /* BT Coex related changes */ + if (hw_priv->is_BT_Present) { + if (((hw_priv->conf_listen_interval * 100) % + bss->beacon_interval) == 0) + priv->listen_interval = + ((hw_priv->conf_listen_interval * 100) / + bss->beacon_interval); + else + priv->listen_interval = + ((hw_priv->conf_listen_interval * 100) / + bss->beacon_interval + 1); + } + + if (tim && tim->dtim_period > 1) { + join.dtimPeriod = tim->dtim_period; + priv->join_dtim_period = tim->dtim_period; + } + priv->beacon_int = bss->beacon_interval; + bes2600_info(BES2600_DBG_STA, "[STA] Join DTIM: %d, interval: %d\n", + join.dtimPeriod, priv->beacon_int); + + hw_priv->is_go_thru_go_neg = false; + join.channelNumber = hw_priv->channel->hw_value; + + /* basicRateSet will be updated after association. + Currently these values are hardcoded */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if (hw_priv->channel->band == NL80211_BAND_5GHZ) { +#else + if (hw_priv->channel->band == IEEE80211_BAND_5GHZ) { +#endif + join.band = WSM_PHY_BAND_5G; + join.basicRateSet = 64; /*6 mbps*/ + }else{ + join.band = WSM_PHY_BAND_2_4G; + join.basicRateSet = 7; /*1, 2, 5.5 mbps*/ + } + memcpy(&join.bssid[0], bssid, sizeof(join.bssid)); + memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid)); + + if (ssidie) { + join.ssidLength = ssidie[1]; + if (WARN_ON(join.ssidLength > sizeof(join.ssid))) + join.ssidLength = sizeof(join.ssid); + memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); + if(strstr(&join.ssid[0],"5.1.4")) + msleep(200); +#ifdef ROAM_OFFLOAD + if((priv->vif->type == NL80211_IFTYPE_STATION)) { + priv->ssid_length = join.ssidLength; + memcpy(priv->ssid, &join.ssid[0], priv->ssid_length); + } +#endif /*ROAM_OFFLOAD*/ + } + + if (priv->vif->p2p) { + join.flags |= WSM_JOIN_FLAGS_P2P_GO; +#ifdef P2P_MULTIVIF + join.flags |= (1 << 6); +#endif + join.basicRateSet = + bes2600_rate_mask_to_wsm(hw_priv, 0xFF0); + } + + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_JOIN); + wsm_flush_tx(hw_priv); + + /* Queue unjoin if not associated in 3 sec. */ + queue_delayed_work(hw_priv->workqueue, + &priv->join_timeout, 3 * HZ); + + bes2600_disable_listening(priv); + + //WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id)); + WARN_ON(wsm_set_block_ack_policy(hw_priv, + 0, hw_priv->ba_tid_mask, priv->if_id)); + spin_lock_bh(&hw_priv->ba_lock); + hw_priv->ba_ena = false; + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_hist = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + spin_unlock_bh(&hw_priv->ba_lock); + + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; + mgmt_policy.encryptionForAuthFrame = 1; + wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, + priv->if_id); + + /* need to switch channel before join */ + channel.channelMode = NL80211_CHAN_NO_HT << 4; + channel.channelSwitchCount = 0; + channel.newChannelNumber = conf->chandef.chan->hw_value; + wsm_switch_channel(hw_priv, &channel, priv->if_id); + + /* avoid lmac assert when wpa_supplicant connect to ap without scan */ + probe_tmp.skb = ieee80211_probereq_get(hw_priv->hw, priv->vif->addr, NULL, 0, 0); + if (probe_tmp.skb) { +#ifdef P2P_MULTIVIF + wsm_set_template_frame(hw_priv, &probe_tmp, 0); +#else + wsm_set_template_frame(hw_priv, &probe_tmp, priv->if_id); +#endif + } + + if (wsm_join(hw_priv, &join, priv->if_id)) { + memset(&priv->join_bssid[0], + 0, sizeof(priv->join_bssid)); +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id); +#else + bes2600_queue_remove(queue, hw_priv->pending_frame_id); +#endif /*CONFIG_BES2600_TESTMODE*/ + cancel_delayed_work_sync(&priv->join_timeout); + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN); + } else { + /* Upload keys */ +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_requeue(hw_priv, queue, + hw_priv->pending_frame_id, true); +#else + bes2600_queue_requeue(queue, hw_priv->pending_frame_id, + true); +#endif + priv->join_status = BES2600_JOIN_STATUS_STA; + atomic_set(&priv->connect_in_process, 1); + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one */ + priv->disable_beacon_filter = true; + + } + bes2600_update_filtering(priv); + } + up(&hw_priv->conf_lock); + if (bss) + cfg80211_put_bss(hw_priv->hw->wiphy, bss); + wsm_unlock_tx(hw_priv); +} + +void bes2600_join_timeout(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, join_timeout.work); + bes2600_info(BES2600_DBG_STA, "[WSM] Issue unjoin command (TMO).\n"); + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN); + atomic_set(&priv->connect_in_process, 0); + wsm_lock_tx(priv->hw_priv); + bes2600_unjoin_work(&priv->unjoin_work); +} + +void bes2600_unjoin_work(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, unjoin_work); + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + struct wsm_reset reset = { + .reset_statistics = true, + }; + bool is_htcapie = false; + int i; + struct bes2600_vif *tmp_priv; + + del_timer_sync(&hw_priv->ba_timer); + down(&hw_priv->conf_lock); + if (unlikely(atomic_read(&hw_priv->scan.in_progress) + || atomic_read(&priv->connect_in_process))) { + if (priv->delayed_unjoin) { + wiphy_dbg(priv->hw->wiphy, + "%s: Delayed unjoin " + "is already scheduled.\n", + __func__); + wsm_unlock_tx(hw_priv); + } else { + bes2600_info(BES2600_DBG_AP, "delay unjoin work, scan:%d connect:%d\n", + atomic_read(&hw_priv->scan.in_progress), atomic_read(&priv->connect_in_process)); + priv->delayed_unjoin = true; + } + up(&hw_priv->conf_lock); + return; + } + + if (priv->join_status && + priv->join_status > BES2600_JOIN_STATUS_STA) { + wiphy_err(priv->hw->wiphy, + "%s: Unexpected: join status: %d\n", + __func__, priv->join_status); + BUG_ON(1); + } + if (priv->join_status) { + cancel_work_sync(&priv->update_filtering_work); + cancel_work_sync(&priv->set_beacon_wakeup_period_work); + memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); +#ifdef BES2600_TX_RX_OPT + txrx_opt_timer_exit(hw_priv); +#endif + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE); + bes2600_pwr_clear_ap_lp_bad_mark(hw_priv); + priv->join_status = BES2600_JOIN_STATUS_PASSIVE; + + /* Unjoin is a reset. */ + wsm_flush_tx(hw_priv); + WARN_ON(wsm_keep_alive_period(hw_priv, 0, priv->if_id)); + WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id)); + WARN_ON(wsm_set_output_power(hw_priv, + hw_priv->output_power * 10, priv->if_id)); + priv->join_dtim_period = 0; + priv->cipherType = 0; + WARN_ON(bes2600_setup_mac_pvif(priv)); + bes2600_free_event_queue(hw_priv); + cancel_work_sync(&hw_priv->event_handler); + cancel_delayed_work_sync(&priv->connection_loss_work); + WARN_ON(wsm_set_block_ack_policy(hw_priv, + 0, hw_priv->ba_tid_mask, priv->if_id)); + priv->disable_beacon_filter = false; + bes2600_update_filtering(priv); + priv->setbssparams_done = false; + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + memset(&priv->firmware_ps_mode, 0, + sizeof(priv->firmware_ps_mode)); + priv->htcap = false; + bes2600_for_each_vif(hw_priv, tmp_priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv) +#else + if (!tmp_priv) +#endif + continue; + if ((tmp_priv->join_status == BES2600_JOIN_STATUS_STA) && tmp_priv->htcap) + is_htcapie = true; + } + + if (is_htcapie) { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE; + bes2600_info(BES2600_DBG_STA, "UNJOIN HTCAP 11N %d\n",hw_priv->vif0_throttle); + } else { + hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE; + bes2600_info(BES2600_DBG_STA, "UNJOIN 11BG %d\n",hw_priv->vif0_throttle); + } + bes2600_info(BES2600_DBG_STA, "[STA] Unjoin.\n"); + } + + up(&hw_priv->conf_lock); + wsm_unlock_tx(hw_priv); +} + +int bes2600_enable_listening(struct bes2600_vif *priv, + struct ieee80211_channel *chan) +{ + /* TODO:COMBO: Channel is common to HW currently in mac80211. + Change the code below once channel is made per VIF */ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct wsm_start start = { +#ifdef P2P_MULTIVIF + .mode = WSM_START_MODE_P2P_DEV | (priv->if_id ? (5 << 4) : 0), +#else + .mode = WSM_START_MODE_P2P_DEV | (priv->if_id << 4), +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + .band = (chan->band == NL80211_BAND_5GHZ) ? +#else + .band = (chan->band == IEEE80211_BAND_5GHZ) ? +#endif + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = chan->hw_value, + .beaconInterval = 100, + .DTIMPeriod = 1, + .probeDelay = 0, + .basicRateSet = 0x0F, + }; + if(priv->if_id != 2) { + //WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR); + return -EOPNOTSUPP; + } + if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) + return 0; + if (priv->join_status == BES2600_JOIN_STATUS_PASSIVE) + priv->join_status = BES2600_JOIN_STATUS_MONITOR; + + WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR); + bes2600_info(BES2600_DBG_STA, "bes2600_enable_listening if_id:%d\n", priv->if_id); + return wsm_start(hw_priv, &start, CW12XX_GENERIC_IF_ID); +} + +int bes2600_disable_listening(struct bes2600_vif *priv) +{ + int ret; + struct wsm_reset reset = { + .reset_statistics = true, + }; + if(priv->if_id != 2) { + WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR); + return 0; + } + priv->join_status = BES2600_JOIN_STATUS_PASSIVE; + + WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR); + + if (priv->hw_priv->roc_if_id == -1) + return 0; + bes2600_info(BES2600_DBG_STA, "bes2600_disable_listening if_id:%d\n", priv->if_id); + ret = wsm_reset(priv->hw_priv, &reset, CW12XX_GENERIC_IF_ID); + return ret; +} + +/* TODO:COMBO:UAPSD will be supported only on one interface */ +int bes2600_set_uapsd_param(struct bes2600_vif *priv, + const struct wsm_edca_params *arg) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + int ret; + u16 uapsdFlags = 0; + + /* Here's the mapping AC [queue, bit] + VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/ + + if (arg->params[0].uapsdEnable) + uapsdFlags |= 1 << 3; + + if (arg->params[1].uapsdEnable) + uapsdFlags |= 1 << 2; + + if (arg->params[2].uapsdEnable) + uapsdFlags |= 1 << 1; + + if (arg->params[3].uapsdEnable) + uapsdFlags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 */ + + priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags); + priv->uapsd_info.minAutoTriggerInterval = 0; + priv->uapsd_info.maxAutoTriggerInterval = 0; + priv->uapsd_info.autoTriggerStep = 0; + + ret = wsm_set_uapsd_info(hw_priv, &priv->uapsd_info, + priv->if_id); + return ret; +} + +void bes2600_ba_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, ba_work); + u8 tx_ba_tid_mask; + + /* TODO:COMBO: reenable this part of code */ +/* if (priv->join_status != BES2600_JOIN_STATUS_STA) + return; + if (!priv->setbssparams_done) + return;*/ + + bes2600_info(BES2600_DBG_STA, "BA work****\n"); + spin_lock_bh(&hw_priv->ba_lock); +// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0; + tx_ba_tid_mask = hw_priv->ba_tid_mask; + spin_unlock_bh(&hw_priv->ba_lock); + + wsm_lock_tx(hw_priv); + + WARN_ON(wsm_set_block_ack_policy(hw_priv, + tx_ba_tid_mask, hw_priv->ba_tid_mask, -1)); /*TODO:COMBO*/ + + wsm_unlock_tx(hw_priv); +} +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) +void bes2600_ba_timer(struct timer_list *t) +{ + bool ba_ena; + struct bes2600_common *hw_priv = from_timer(hw_priv, t, ba_timer); + + spin_lock_bh(&hw_priv->ba_lock); + bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc, + hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx); + + if (atomic_read(&hw_priv->scan.in_progress)) { + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + goto skip_statistic_update; + } + + if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT && + (hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD || + (hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT && + hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >= + BES2600_BLOCK_ACK_THLD))) + ba_ena = true; + else + ba_ena = false; + + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + + if (ba_ena != hw_priv->ba_ena) { + if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) { + hw_priv->ba_ena = ba_ena; + hw_priv->ba_hist = 0; +#if 0 + bes2600_dbg(BES2600_DBG_STA, "[STA] %s block ACK:\n", + ba_ena ? "enable" : "disable"); + queue_work(hw_priv->workqueue, &hw_priv->ba_work); +#endif + } + } else if (hw_priv->ba_hist) + --hw_priv->ba_hist; + +skip_statistic_update: + spin_unlock_bh(&hw_priv->ba_lock); +} + +#else +void bes2600_ba_timer(unsigned long arg) +{ + bool ba_ena; + struct bes2600_common *hw_priv = (struct bes2600_common *)arg; + + spin_lock_bh(&hw_priv->ba_lock); + bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc, + hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx); + + if (atomic_read(&hw_priv->scan.in_progress)) { + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + goto skip_statistic_update; + } + + if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT && + (hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD || + (hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT && + hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >= + BES2600_BLOCK_ACK_THLD))) + ba_ena = true; + else + ba_ena = false; + + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + + if (ba_ena != hw_priv->ba_ena) { + if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) { + hw_priv->ba_ena = ba_ena; + hw_priv->ba_hist = 0; +#if 0 + bes2600_dbg(BES2600_DBG_STA, "[STA] %s block ACK:\n", + ba_ena ? "enable" : "disable"); + queue_work(hw_priv->workqueue, &hw_priv->ba_work); +#endif + } + } else if (hw_priv->ba_hist) + --hw_priv->ba_hist; + +skip_statistic_update: + spin_unlock_bh(&hw_priv->ba_lock); +} +#endif +int bes2600_vif_setup(struct bes2600_vif *priv) +{ + struct bes2600_common *hw_priv = priv->hw_priv; + int ret = 0; + + /* Setup per vif workitems and locks */ + spin_lock_init(&priv->vif_lock); + INIT_WORK(&priv->join_work, bes2600_join_work); + INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout); + INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work); + INIT_WORK(&priv->wep_key_work, bes2600_wep_key_work); + INIT_WORK(&priv->offchannel_work, bes2600_offchannel_work); + INIT_DELAYED_WORK(&priv->bss_loss_work, bes2600_bss_loss_work); + INIT_DELAYED_WORK(&priv->connection_loss_work, + bes2600_connection_loss_work); + spin_lock_init(&priv->bss_loss_lock); + INIT_WORK(&priv->tx_failure_work, bes2600_tx_failure_work); + spin_lock_init(&priv->ps_state_lock); + INIT_DELAYED_WORK(&priv->set_cts_work, bes2600_set_cts_work); + INIT_WORK(&priv->set_tim_work, bes2600_set_tim_work); + INIT_WORK(&priv->multicast_start_work, bes2600_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, bes2600_multicast_stop_work); + INIT_WORK(&priv->link_id_work, bes2600_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, bes2600_link_id_gc_work); +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + INIT_WORK(&priv->linkid_reset_work, bes2600_link_id_reset); +#endif + INIT_WORK(&priv->update_filtering_work, bes2600_update_filtering_work); + INIT_DELAYED_WORK(&priv->pending_offchanneltx_work, + bes2600_pending_offchanneltx_work); + INIT_WORK(&priv->set_beacon_wakeup_period_work, + bes2600_set_beacon_wakeup_period_work); +#ifdef AP_HT_CAP_UPDATE + INIT_WORK(&priv->ht_info_update_work, bes2600_ht_info_update_work); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + timer_setup(&priv->mcast_timeout, bes2600_mcast_timeout, 0); +#else + setup_timer(&priv->mcast_timeout, bes2600_mcast_timeout, (unsigned long)priv); +#endif + + priv->setbssparams_done = false; + priv->power_set_true = 0; + priv->user_power_set_true = 0; + priv->user_pm_mode = 0; + ret = bes2600_debug_init_priv(hw_priv, priv); + if (WARN_ON(ret)) + goto out; + + /* Initialising the broadcast filter */ + memset(priv->broadcast_filter.MacAddr, 0xFF, ETH_ALEN); + priv->broadcast_filter.nummacaddr = 1; + priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_A1; + priv->broadcast_filter.filter_mode = WSM_FILTER_ACTION_FILTER_OUT; + priv->htcap = false; + + bes2600_info(BES2600_DBG_STA, "%s: enabling priv\n", __func__); + atomic_set(&priv->enabled, 1); + + spin_lock(&hw_priv->vif_list_lock); + hw_priv->if_id_slot |= BIT(priv->if_id); + hw_priv->vif_list[priv->if_id] = priv->vif; + atomic_inc(&hw_priv->num_vifs); + spin_unlock(&hw_priv->vif_list_lock); + atomic_set(&priv->connect_in_process, 0); + +#ifdef P2P_MULTIVIF + if (priv->if_id < 2) { +#endif + /* default EDCA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, + 47, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, + 94, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, + 0, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, + 0, 0xc8, false); + ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); + if (WARN_ON(ret)) + goto out; + + ret = bes2600_set_uapsd_param(priv, &priv->edca); + if (WARN_ON(ret)) + goto out; + + memset(priv->bssid, ~0, ETH_ALEN); + priv->wep_default_key_id = -1; + priv->cipherType = 0; + #ifdef P2P_STA_COEX + priv->cqm_link_loss_count = 400; + priv->cqm_beacon_loss_count = 200; + #else + priv->cqm_link_loss_count = 100; + priv->cqm_beacon_loss_count = 50; + #endif + + /* Temporary configuration - beacon filter table */ + __bes2600_bf_configure(priv); +#ifdef P2P_MULTIVIF + } +#endif +out: + return ret; +} + +int bes2600_setup_mac_pvif(struct bes2600_vif *priv) +{ + int ret = 0; + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ + /* NOTE2: RSSI based reports have been switched to RCPI, since + * FW has a bug and RSSI reported values are not stable, + * what can leads to signal level oscilations in user-end applications */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + + /* Remember the decission here to make sure, we will handle + * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */ + if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) + priv->cqm_use_rssi = true; + + + /* Configure RSSI/SCPI reporting as RSSI. */ +#ifdef P2P_MULTIVIF + ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, + priv->if_id ? 0 : 0); +#else + ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, + priv->if_id); +#endif + return ret; +} + +void bes2600_rem_chan_timeout(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, rem_chan_timeout.work); + int ret, if_id; + struct bes2600_vif *priv; + + if (atomic_read(&hw_priv->remain_on_channel) == 0) { + return; + } + ieee80211_remain_on_channel_expired(hw_priv->hw); + + down(&hw_priv->conf_lock); + if_id = hw_priv->roc_if_id; + bes2600_dbg(BES2600_DBG_ROC, "ROC TO IN %d\n", if_id); + priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id); + ret = WARN_ON(__bes2600_flush(hw_priv, false, if_id)); + + if (!ret) { + wsm_unlock_tx(hw_priv); + bes2600_disable_listening(priv); + } + atomic_set(&hw_priv->remain_on_channel, 0); + hw_priv->roc_if_id = -1; + + bes2600_dbg(BES2600_DBG_ROC, "ROC TO OUT %d\n", if_id); + + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC); +} + +void bes2600_dynamic_opt_txrx_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, dynamic_opt_txrx_work); + struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0); + bes2600_dynamic_opt_rxtx(hw_priv,priv, 0); + bes2600_dbg(BES2600_DBG_STA, "bes2600_dynamic_opt_txrx_work called\n"); +} + + +const u8 *bes2600_get_ie(u8 *start, size_t len, u8 ie) +{ + u8 *end, *pos; + + pos = start; + if (pos == NULL) + return NULL; + end = pos + len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + +/** + * bes2600_set_macaddrfilter -called when tesmode command + * is for setting mac address filter + * + * @hw: the hardware + * @data: incoming data + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_set_macaddrfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data) +{ + struct wsm_mac_addr_filter *mac_addr_filter = NULL; + struct wsm_mac_addr_info *addr_info = NULL; + u8 action_mode = 0, no_of_mac_addr = 0, i = 0; + int ret = 0; + u16 macaddrfiltersize = 0; + + /* Retrieving Action Mode */ + action_mode = data[0]; + /* Retrieving number of address entries */ + no_of_mac_addr = data[1]; + + addr_info = (struct wsm_mac_addr_info *)&data[2]; + + /* Computing sizeof Mac addr filter */ + macaddrfiltersize = sizeof(*mac_addr_filter) + \ + (no_of_mac_addr * sizeof(struct wsm_mac_addr_info)); + + mac_addr_filter = kzalloc(macaddrfiltersize, GFP_KERNEL); + if (!mac_addr_filter) { + ret = -ENOMEM; + goto exit_p; + } + mac_addr_filter->action_mode = action_mode; + mac_addr_filter->numfilter = no_of_mac_addr; + + for (i = 0; i < no_of_mac_addr; i++) { + mac_addr_filter->macaddrfilter[i].address_mode = \ + addr_info[i].address_mode; + memcpy(mac_addr_filter->macaddrfilter[i].MacAddr, \ + addr_info[i].MacAddr , ETH_ALEN); + mac_addr_filter->macaddrfilter[i].filter_mode = \ + addr_info[i].filter_mode; + } + ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_MAC_ADDR_FILTER, \ + mac_addr_filter, macaddrfiltersize, priv->if_id)); + + kfree(mac_addr_filter); +exit_p: + return ret; +} + +#if 0 +/** + * bes2600_set_multicastaddrfilter -called when tesmode command + * is for setting the ipv4 address filter + * + * @hw: the hardware + * @data: incoming data + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_set_multicastfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data) +{ + u8 i = 0; + int ret = 0; + + memset(&priv->multicast_filter, 0, sizeof(priv->multicast_filter)); + priv->multicast_filter.enable = (u32)data[0]; + priv->multicast_filter.numOfAddresses = (u32)data[1]; + + for (i = 0; i < priv->multicast_filter.numOfAddresses; i++) { + memcpy(&priv->multicast_filter.macAddress[i], \ + &data[2+(i*ETH_ALEN)], ETH_ALEN); + } + /* Configure the multicast mib in case of drop all multicast */ + if (priv->multicast_filter.enable != 2) + return ret; + + ret = wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, \ + &priv->multicast_filter, sizeof(priv->multicast_filter), priv->if_id); + + return ret; +} +#endif + +#ifdef IPV6_FILTERING +/** + * bes2600_set_ipv6addrfilter -called when tesmode command + * is for setting the ipv6 address filter + * + * @hw: the hardware + * @data: incoming data + * @if_id: interface id + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_set_ipv6addrfilter(struct ieee80211_hw *hw, + u8 *data, int if_id) +{ + struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv; + struct wsm_ipv6_filter *ipv6_filter = NULL; + struct ipv6_addr_info *ipv6_info = NULL; + u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0; + u16 ipaddrfiltersize = 0; + + /* Retrieving Action Mode */ + action_mode = data[0]; + /* Retrieving number of ipv4 address entries */ + no_of_ip_addr = data[1]; + + ipv6_info = (struct ipv6_addr_info *)&data[2]; + + /* Computing sizeof Mac addr filter */ + ipaddrfiltersize = sizeof(*ipv6_filter) + \ + (no_of_ip_addr * sizeof(struct wsm_ip6_addr_info)); + + + ipv6_filter = kzalloc(ipaddrfiltersize, GFP_KERNEL); + if (!ipv6_filter) { + ret = -ENOMEM; + goto exit_p; + } + ipv6_filter->action_mode = action_mode; + ipv6_filter->numfilter = no_of_ip_addr; + + for (i = 0; i < no_of_ip_addr; i++) { + ipv6_filter->ipv6filter[i].address_mode = \ + ipv6_info[i].address_mode; + ipv6_filter->ipv6filter[i].filter_mode = \ + ipv6_info[i].filter_mode; + memcpy(ipv6_filter->ipv6filter[i].ipv6, \ + (u8 *)(ipv6_info[i].ipv6), 16); + } + + ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_IP_IPV6_ADDR_FILTER, \ + ipv6_filter, ipaddrfiltersize, \ + if_id)); + + kfree(ipv6_filter); +exit_p: + return ret; +} +#endif /*IPV6_FILTERING*/ + +/** + * bes2600_set_data_filter -configure data filter in device +* + * @hw: the hardware + * @vif: vif + * @data: incoming data + * @len: incoming data length + * + */ +void bes2600_set_data_filter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + int ret = 0; + //struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + int filter_id; + + if (!data) { + ret = -EINVAL; + goto exit_p; + } + filter_id=*((enum bes2600_data_filterid*)data); + + switch (filter_id) { +#ifdef IPV6_FILTERING + case IPV6ADDR_FILTER_ID: + ret = bes2600_set_ipv6addrfilter(hw, \ + &((u8 *)data)[4], priv->if_id); + break; +#endif /*IPV6_FILTERING*/ + default: + ret = -EINVAL; + break; + } +exit_p: + + return ; +} + +u32 bes2600_bh_get_encry_hdr_len(u32 cipherType) +{ + u32 encrypthdr = 0; + + switch (cipherType) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + encrypthdr = WEP_ENCRYPT_HDR_SIZE; + break; + case WLAN_CIPHER_SUITE_TKIP: + encrypthdr = WPA_ENCRYPT_HDR_SIZE; + break; + case WLAN_CIPHER_SUITE_CCMP: + encrypthdr = WPA2_ENCRYPT_HDR_SIZE; + break; + case WLAN_CIPHER_SUITE_SMS4: + encrypthdr = WAPI_ENCRYPT_HDR_SIZE; + break; + default: + encrypthdr = 0; + break; + } + + return encrypthdr; +} + +/** + * bes2600_set_arpreply -called for creating and + * configuring arp response template frame + * + * @hw: the hardware + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_common *hw_priv = (struct bes2600_common *)hw->priv; + u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0; + bool encrypt = false; + int ret = 0; + u8 *template_frame = NULL; + struct ieee80211_hdr_3addr *dot11hdr = NULL; + struct ieee80211_snap_hdr *snaphdr = NULL; + struct arphdr *arp_hdr = NULL; + + template_frame = kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, GFP_ATOMIC); + if (!template_frame) { + bes2600_err(BES2600_DBG_STA, "[STA] Template frame memory failed\n"); + ret = -ENOMEM; + goto exit_p; + } + dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4]; + + framehdrlen = sizeof(*dot11hdr); + if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) + priv->cipherType = WLAN_CIPHER_SUITE_CCMP; + switch (priv->cipherType) { + + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + bes2600_dbg(BES2600_DBG_STA, "[STA] WEP\n"); + encrypthdr = WEP_ENCRYPT_HDR_SIZE; + encrypttailsize = WEP_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + + case WLAN_CIPHER_SUITE_TKIP: + bes2600_dbg(BES2600_DBG_STA, "[STA] WPA\n"); + encrypthdr = WPA_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_CCMP: + bes2600_dbg(BES2600_DBG_STA, "[STA] WPA2\n"); + encrypthdr = WPA2_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_SMS4: + bes2600_dbg(BES2600_DBG_STA, "[STA] WAPI\n"); + encrypthdr = WAPI_ENCRYPT_HDR_SIZE; + encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + default: + encrypthdr = 0; + encrypttailsize = 0; + encrypt = 0; + break; + } + + framehdrlen += encrypthdr; + + /* Filling the 802.11 Hdr */ + dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + if (priv->vif->type == NL80211_IFTYPE_STATION) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS); + else + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + + if (encrypt) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP); + + if (priv->vif->bss_conf.qos) { + bes2600_dbg(BES2600_DBG_STA, "[STA] QOS Enabled\n"); + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP); + *(u16 *)(dot11hdr + 1) = 0x0; + framehdrlen += 2; + } else { + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA); + } + + memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); + memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); + + /* Filling the LLC/SNAP Hdr */ + snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen); + memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \ + sizeof(*snaphdr)); + *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_ARP); + /* Updating the framebdylen with snaphdr and LLC hdr size */ + framebdylen = sizeof(*snaphdr) + 2; + + /* Filling the ARP Reply Payload */ + arp_hdr = (struct arphdr *)((u8 *)dot11hdr + framehdrlen + framebdylen); + arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); + arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); + arp_hdr->ar_hln = ETH_ALEN; + arp_hdr->ar_pln = 4; + arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); + + /* Updating the frmbdylen with Arp Reply Hdr and Arp payload size(20) */ + framebdylen += sizeof(*arp_hdr) + 20; + + /* Updating the framebdylen with Encryption Tail Size */ + framebdylen += encrypttailsize; + + /* Filling the Template Frame Hdr */ + template_frame[0] = WSM_FRAME_TYPE_ARP_REPLY; /* Template frame type */ + template_frame[1] = 0xFF; /* Rate to be fixed */ + ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen; + + ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \ + template_frame, (framehdrlen+framebdylen+4), priv->if_id)); + kfree(template_frame); +exit_p: + return ret; +} + +#ifdef IPV6_FILTERING +/** + * bes2600_set_na -called for creating and + * configuring NDP Neighbor Advertisement (NA) template frame + * + * @hw: the hardware + * @vif: vif + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_set_na(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_common *hw_priv = (struct bes2600_common *)hw->priv; + u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0; + bool encrypt = false; + int ret = 0; + u8 *template_frame = NULL; + struct ieee80211_hdr_3addr *dot11hdr = NULL; + struct ieee80211_snap_hdr *snaphdr = NULL; + struct ipv6hdr *ipv6_hdr = NULL; + struct icmp6hdr *icmp6_hdr = NULL; + struct nd_msg *na = NULL; + struct nd_opt_hdr *opt_hdr = NULL; + + template_frame = kzalloc(MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE, GFP_ATOMIC); + if (!template_frame) { + bes2600_err(BES2600_DBG_STA, "[STA] Template frame memory failed\n"); + ret = -ENOMEM; + goto exit_p; + } + dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4]; + + framehdrlen = sizeof(*dot11hdr); + if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) + priv->cipherType = WLAN_CIPHER_SUITE_CCMP; + switch (priv->cipherType) { + + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + bes2600_dbg(BES2600_DBG_STA, "[STA] WEP\n"); + encrypthdr = WEP_ENCRYPT_HDR_SIZE; + encrypttailsize = WEP_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + + case WLAN_CIPHER_SUITE_TKIP: + bes2600_dbg(BES2600_DBG_STA, "[STA] WPA\n"); + encrypthdr = WPA_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_CCMP: + bes2600_dbg(BES2600_DBG_STA, "[STA] WPA2\n"); + encrypthdr = WPA2_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_SMS4: + bes2600_dbg(BES2600_DBG_STA, "[STA] WAPI\n"); + encrypthdr = WAPI_ENCRYPT_HDR_SIZE; + encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + default: + encrypthdr = 0; + encrypttailsize = 0; + encrypt = 0; + break; + } + + framehdrlen += encrypthdr; + + /* Filling the 802.11 Hdr */ + dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + if (priv->vif->type == NL80211_IFTYPE_STATION) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS); + else + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + + if (encrypt) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP); + + if (priv->vif->bss_conf.qos) { + bes2600_dbg(BES2600_DBG_STA, "[STA] QOS Enabled\n"); + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP); + /* Filling QOS Control Field */ + *(u16 *)(dot11hdr + 1) = 0x0; + framehdrlen += 2; + } else { + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA); + } + + memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); + memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); + + /* Filling the LLC/SNAP Hdr */ + snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen); + memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \ + sizeof(*snaphdr)); + *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IPV6); + /* Updating the framebdylen with snaphdr and LLC hdr size */ + framebdylen = sizeof(*snaphdr) + 2; + + /* Filling the ipv6 header */ + ipv6_hdr = (struct ipv6hdr *)((u8 *)dot11hdr + framehdrlen + framebdylen); + ipv6_hdr->version = 6; + ipv6_hdr->priority = 0; + ipv6_hdr->payload_len = cpu_to_be16(32); /* ??? check the be or le ??? whether to use cpu_to_be16(32)*/ + ipv6_hdr->nexthdr = 58; + ipv6_hdr->hop_limit = 255; + + /* Updating the framebdylen with ipv6 Hdr */ + framebdylen += sizeof(*ipv6_hdr); + + /* Filling the Neighbor Advertisement */ + na = (struct nd_msg *)((u8 *)dot11hdr + framehdrlen + framebdylen); + icmp6_hdr = (struct icmp6hdr *)(&na->icmph); + icmp6_hdr->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; + icmp6_hdr->icmp6_code = 0; + /* checksum (2 bytes), RSO fields (4 bytes) and target IP address (16 bytes) shall be filled by firmware */ + + /* Filling the target link layer address in the optional field */ + opt_hdr = (struct nd_opt_hdr *)(&na->opt[0]); + opt_hdr->nd_opt_type = 2; + opt_hdr->nd_opt_len = 1; + /* optional target link layer address (6 bytes) shall be filled by firmware */ + + /* Updating the framebdylen with the ipv6 payload length */ + framebdylen += 32; + + /* Updating the framebdylen with Encryption Tail Size */ + framebdylen += encrypttailsize; + + /* Filling the Template Frame Hdr */ + template_frame[0] = WSM_FRAME_TYPE_NA; /* Template frame type */ + template_frame[1] = 0xFF; /* Rate to be fixed */ + ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen; + + ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \ + template_frame, (framehdrlen+framebdylen+4), \ + priv->if_id)); + + kfree(template_frame); + +exit_p: + return ret; +} +#endif /*IPV6_FILTERING*/ + +#ifdef CONFIG_BES2600_TESTMODE +/** + * bes2600_set_snap_frame -Set SNAP frame format + * + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_set_snap_frame(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct bes_msg_set_snap_frame *snap_frame = + (struct bes_msg_set_snap_frame *) data; + struct bes2600_common *priv = (struct bes2600_common *) hw->priv; + u8 frame_len = snap_frame->len; + u8 *frame = &snap_frame->frame[0]; + + /* + * Check length of incoming frame format: + * SNAP + SNAP_LEN (u8) + */ + if (frame_len + sizeof(snap_frame->len) != len) + return -EINVAL; + + if (frame_len > 0) { + priv->test_frame.data = (u8 *) krealloc(priv->test_frame.data, + sizeof(u8) * frame_len, + GFP_KERNEL); + if (priv->test_frame.data == NULL) { + bes2600_dbg(BES2600_DBG_TEST_MODE, "bes2600_set_snap_frame memory" \ + "allocation failed"); + priv->test_frame.len = 0; + return -EINVAL; + } + memcpy(priv->test_frame.data, frame, frame_len); + } else { + kfree(priv->test_frame.data); + priv->test_frame.data = NULL; + } + priv->test_frame.len = frame_len; + return 0; +} + + +/** + * bes2600_set_txqueue_params -Set txqueue params after successful TSPEC negotiation + * + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_set_txqueue_params(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct bes_msg_set_txqueue_params *txqueue_params = + (struct bes_msg_set_txqueue_params *) data; + struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv; + struct bes2600_vif *priv; + /* Interface ID is hard coded here, as interface is not + * passed in testmode command. + * Also it is assumed here that STA will be on interface + * 0 always. + */ + + int if_id = 0; + u16 queueId = bes2600_priority_to_queueId[txqueue_params->user_priority]; + + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, if_id); + + if (unlikely(!priv)) { + bes2600_err(BES2600_DBG_TEST_MODE, "[STA] %s: Warning Priv is Null\n", + __func__); + return 0; + } + spin_unlock(&priv->vif_lock); + + /* Default Ack policy is WSM_ACK_POLICY_NORMAL */ + WSM_TX_QUEUE_SET(&priv->tx_queue_params, + queueId, + WSM_ACK_POLICY_NORMAL, + txqueue_params->medium_time, + txqueue_params->expiry_time); + return WARN_ON(wsm_set_tx_queue_params(hw_priv, + &priv->tx_queue_params.params[queueId], queueId, + priv->if_id)); +} + +/** + * bes2600_testmode_reply -called inside a testmode command + * handler to send a response to user space + * + * @wiphy: the wiphy + * @data: data to be send to user space + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_testmode_reply(struct wiphy *wiphy, + const void *data, int len) +{ + int ret = 0; + struct sk_buff *skb = cfg80211_testmode_alloc_reply_skb(wiphy, + nla_total_size(len)); + + bes2600_dbg(BES2600_DBG_TEST_MODE, "%s\n", __func__); + if (!skb) + return -ENOMEM; + + ret = nla_put(skb, BES_TM_MSG_DATA, len, data); + if (ret) { + kfree_skb(skb); + return ret; + } + + return cfg80211_testmode_reply(skb); +} + +/** + * bes2600_testmode_event -send asynchronous event + * to userspace + * + * @wiphy: the wiphy + * @msg_id: BES msg ID + * @data: data to be sent + * @len: data length + * @gfp: allocation flag + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_testmode_event(struct wiphy *wiphy, const u32 msg_id, + const void *data, int len, gfp_t gfp) +{ + struct sk_buff *skb = cfg80211_testmode_alloc_event_skb(wiphy, + nla_total_size(len+sizeof(msg_id)), gfp); + + if (!skb) + return -ENOMEM; + + if (nla_put_u32(skb, BES_TM_MSG_ID, msg_id)) + goto nla_put_failure; + if (data) + if (nla_put(skb, BES_TM_MSG_DATA, len, data)) + goto nla_put_failure; + + cfg80211_testmode_event(skb, gfp); + return 0; +nla_put_failure: + kfree_skb(skb); + return -ENOBUFS; +} + +/** + * example function for test purposes + * sends both: synchronous reply and asynchronous event + */ +static int bes2600_test(struct ieee80211_hw *hw, + void *data, int len) +{ + struct bes_msg_test_t *test_p; + struct bes_reply_test_t reply; + struct bes_event_test_t event; + + if (sizeof(struct bes_msg_test_t) != len) + return -EINVAL; + + test_p = (struct bes_msg_test_t *) data; + + reply.dummy = test_p->dummy + 10; + + event.dummy = test_p->dummy + 20; + + if (bes2600_testmode_event(hw->wiphy, BES_MSG_EVENT_TEST, + &event, sizeof(event), GFP_KERNEL)) + return -1; + + return bes2600_testmode_reply(hw->wiphy, &reply, sizeof(reply)); +} + +/** + * bes2600_get_tx_power_level - send tx power level + * to userspace + * + * @hw: the hardware + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_get_tx_power_level(struct ieee80211_hw *hw) +{ + struct bes2600_common *hw_priv = hw->priv; + int get_power = 0; + get_power = hw_priv->output_power; + bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] %s: Power set on Device : %d", + __func__, get_power); + return bes2600_testmode_reply(hw->wiphy, &get_power, sizeof(get_power)); +} + +/** + * bes2600_get_tx_power_range- send tx power range + * to userspace for each band + * + * @hw: the hardware + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_get_tx_power_range(struct ieee80211_hw *hw) +{ + struct bes2600_common *hw_priv = hw->priv; + struct wsm_tx_power_range txPowerRange[2]; + size_t len = sizeof(txPowerRange); + memcpy(txPowerRange, hw_priv->txPowerRange, len); + return bes2600_testmode_reply(hw->wiphy, txPowerRange, len); +} + +/** + * bes2600_set_advance_scan_elems -Set Advcance Scan + * elements + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_set_advance_scan_elems(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct advance_scan_elems *scan_elems = + (struct advance_scan_elems *) data; + struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv; + size_t elems_len = sizeof(struct advance_scan_elems); + + if (elems_len != len) + return -EINVAL; + + scan_elems = (struct advance_scan_elems *) data; + + /* Locks required to prevent simultaneous scan */ + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); + + hw_priv->advanceScanElems.scanMode = scan_elems->scanMode; + hw_priv->advanceScanElems.duration = scan_elems->duration; + hw_priv->enable_advance_scan = true; + + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + + return 0; +} + +/** + * bes2600_set_power_save -Set Power Save + * elements + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_set_power_save(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct power_save_elems *ps_elems = + (struct power_save_elems *) data; + struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv; + size_t elems_len = sizeof(struct power_save_elems); + struct bes2600_vif *priv; + int if_id = 0; + /* Interface ID is hard coded here, as interface is not + * passed in testmode command. + * Also it is assumed here that STA will be on interface + * 0 always. + */ + + if (elems_len != len) + return -EINVAL; + + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, if_id); + + if (unlikely(!priv)) { + bes2600_err(BES2600_DBG_TEST_MODE, "[STA] %s: Warning Priv is Null\n", + __func__); + return 0; + } + + spin_unlock(&priv->vif_lock); + down(&hw_priv->conf_lock); + + ps_elems = (struct power_save_elems *) data; + + if (ps_elems->powerSave == 1) + priv->user_pm_mode = WSM_PSM_PS; + else + priv->user_pm_mode = WSM_PSM_FAST_PS; + + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] Aid: %d, Joined: %s, Powersave: %s\n", + priv->bss_params.aid, + priv->join_status == BES2600_JOIN_STATUS_STA ? "yes" : "no", + priv->user_pm_mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : + priv->user_pm_mode == WSM_PSM_PS ? "WSM_PSM_PS" : + priv->user_pm_mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : + "UNKNOWN"); + if (priv->join_status == BES2600_JOIN_STATUS_STA && + priv->bss_params.aid && + priv->setbssparams_done && + priv->filter4.enable) { + priv->powersave_mode.pmMode = priv->user_pm_mode; + bes2600_set_pm(priv, &priv->powersave_mode); + } + else + priv->user_power_set_true = ps_elems->powerSave; + up(&hw_priv->conf_lock); + return 0; +} +/** + * bes2600_start_stop_tsm - starts/stops collecting TSM + * + * @hw: the hardware + * @data: data frame + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) +{ + struct bes_msg_start_stop_tsm *start_stop_tsm = + (struct bes_msg_start_stop_tsm *) data; + struct bes2600_common *hw_priv = hw->priv; + hw_priv->start_stop_tsm.start = start_stop_tsm->start; + hw_priv->start_stop_tsm.up = start_stop_tsm->up; + hw_priv->start_stop_tsm.packetization_delay = + start_stop_tsm->packetization_delay; + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: start : %u: up : %u", + __func__, hw_priv->start_stop_tsm.start, + hw_priv->start_stop_tsm.up); + hw_priv->tsm_info.ac = bes2600_1d_to_ac[start_stop_tsm->up]; + + if (!hw_priv->start_stop_tsm.start) { + spin_lock_bh(&hw_priv->tsm_lock); + memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats)); + memset(&hw_priv->tsm_info, 0, sizeof(hw_priv->tsm_info)); + spin_unlock_bh(&hw_priv->tsm_lock); + } + return 0; +} + +/** + * bes2600_get_tsm_params - Retrieves TSM parameters + * + * @hw: the hardware + * + * Returns: TSM parameters collected + */ +int bes2600_get_tsm_params(struct ieee80211_hw *hw) +{ + struct bes2600_common *hw_priv = hw->priv; + struct bes_tsm_stats tsm_stats; + u32 pkt_count; + spin_lock_bh(&hw_priv->tsm_lock); + pkt_count = hw_priv->tsm_stats.txed_msdu_count - + hw_priv->tsm_stats.msdu_discarded_count; + if (pkt_count) { + hw_priv->tsm_stats.avg_q_delay = + hw_priv->tsm_info.sum_pkt_q_delay/(pkt_count * 1000); + hw_priv->tsm_stats.avg_transmit_delay = + hw_priv->tsm_info.sum_media_delay/pkt_count; + } else { + hw_priv->tsm_stats.avg_q_delay = 0; + hw_priv->tsm_stats.avg_transmit_delay = 0; + } + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Txed MSDU count : %u", + __func__, hw_priv->tsm_stats.txed_msdu_count); + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Average queue delay : %u", + __func__, hw_priv->tsm_stats.avg_q_delay); + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Average transmit delay : %u", + __func__, hw_priv->tsm_stats.avg_transmit_delay); + memcpy(&tsm_stats, &hw_priv->tsm_stats, sizeof(hw_priv->tsm_stats)); + /* Reset the TSM statistics */ + memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats)); + hw_priv->tsm_info.sum_pkt_q_delay = 0; + hw_priv->tsm_info.sum_media_delay = 0; + spin_unlock_bh(&hw_priv->tsm_lock); + return bes2600_testmode_reply(hw->wiphy, &tsm_stats, + sizeof(hw_priv->tsm_stats)); +} + +/** + * bes2600_get_roam_delay - Retrieves roam delay + * + * @hw: the hardware + * + * Returns: Returns the last measured roam delay + */ +int bes2600_get_roam_delay(struct ieee80211_hw *hw) +{ + struct bes2600_common *hw_priv = hw->priv; + u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000; + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Roam delay : %u", + __func__, roam_delay); + spin_lock_bh(&hw_priv->tsm_lock); + hw_priv->tsm_info.roam_delay = 0; + hw_priv->tsm_info.use_rx_roaming = 0; + spin_unlock_bh(&hw_priv->tsm_lock); + return bes2600_testmode_reply(hw->wiphy, &roam_delay, sizeof(u16)); +} + +/* tcp & udp alive test */ +#ifdef CONFIG_BES2600_KEEP_ALIVE + +int bes2600_set_ipv4addrfilter(struct bes2600_common *hw_priv, u8 *data, int if_id) +{ + struct wsm_ipv4_filter *ipv4_filter = NULL; + struct ipv4_addr_info *ipv4_info = NULL; + u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0; + u16 ipaddrfiltersize = 0; + + /* Retrieving Action Mode */ + action_mode = data[0]; + /* Retrieving number of ipv4 address entries */ + no_of_ip_addr = data[1]; + + ipv4_info = (struct ipv4_addr_info *)&data[2]; + + /* Computing sizeof Mac addr filter */ + ipaddrfiltersize = sizeof(*ipv4_filter) + \ + (no_of_ip_addr * sizeof(struct wsm_ip4_addr_info)); + + + ipv4_filter = kzalloc(ipaddrfiltersize, GFP_KERNEL); + if (!ipv4_filter) { + ret = -ENOMEM; + goto exit_p; + } + ipv4_filter->action_mode = action_mode; + ipv4_filter->numfilter = no_of_ip_addr; + + for (i = 0; i < no_of_ip_addr; i++) { + ipv4_filter->ipv4filter[i].address_mode = \ + ipv4_info[i].address_mode; + ipv4_filter->ipv4filter[i].filter_mode = \ + ipv4_info[i].filter_mode; + memcpy(ipv4_filter->ipv4filter[i].ipv4, \ + (u8 *)(ipv4_info[i].ipv4), 4); + } + + ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_IPV4_ADDR_FILTERING, \ + ipv4_filter, ipaddrfiltersize, \ + if_id)); + + kfree(ipv4_filter); +exit_p: + return ret; +} + + +/* Checksum a block of data */ +static uint16_t csum(uint16_t *packet, int packlen) +{ + uint32_t sum = 0; + + while (packlen > 1) { + sum += *(packet++); + packlen -= 2; + } + + if (packlen > 0) + sum += *(unsigned char *)packet; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return (uint16_t) ~sum; +} + + +static void tcpcsum(struct ip_header *ip, + struct tcp_header *tcp, + uint8_t *data, + uint16_t data_len) +{ + uint8_t tcphd_len = sizeof(struct tcp_header); + uint16_t *buf = kzalloc(12 + tcphd_len + data_len, GFP_KERNEL); + uint8_t *tempbuf = (uint8_t *)buf; + + if(tempbuf == NULL) { + bes2600_err(BES2600_DBG_TEST_MODE, "Out of memory: TCP checksum not computed\n"); + return; + } + + tcp->chksum = 0; + /* Set up the pseudo header */ + memcpy(tempbuf, &(ip->src), sizeof(uint32_t)); + memcpy(&(tempbuf[4]), &(ip->dest), sizeof(uint32_t)); + tempbuf[8] = 0; + tempbuf[9] = (uint16_t)ip->_proto; + tempbuf[10] = (uint16_t)((tcphd_len + data_len) & 0xFF00) >> 8; + tempbuf[11] = (uint16_t)((tcphd_len + data_len) & 0x00FF); + /* Copy the TCP header and data */ + memcpy(tempbuf + 12, (uint8_t *)tcp, tcphd_len); + memcpy(tempbuf + 12 + tcphd_len, data, data_len); + /* CheckSum it */ + tcp->chksum = csum(buf, 12 + tcphd_len + data_len); + kfree(buf); +} + +int bes2600_set_ip_offload(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + struct ip_alive_cfg *iac, + u16 idx) +{ + u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0, tu_len, tu_proto; + bool encrypt = false; + int ret = 0; + u8 *tmp_frame = NULL; + u8 *ptr = NULL; + struct ieee80211_hdr_3addr *dot11hdr = NULL; + struct ieee80211_snap_hdr *snaphdr = NULL; + struct ip_header *ipheader = NULL; + struct tcp_header *tcpheader = NULL; + struct udp_header *udpheader = NULL; + u8 *tcpbody = NULL; + u8 *udpbody = NULL; + u8 EncrType; + + u8 *payload = iac->bd.payload; + u32 bodylen = iac->bd.len; + tmp_frame = kzalloc(MAX_TCP_ALIVE_TEMPLATE_SIZE, GFP_ATOMIC); + if (!tmp_frame) { + bes2600_err(BES2600_DBG_TEST_MODE, "[STA] Template frame memory failed\n"); + ret = -ENOMEM; + goto exit_p; + } + dot11hdr = (struct ieee80211_hdr_3addr *)&tmp_frame[4]; + + framehdrlen = sizeof(*dot11hdr); + if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) + priv->cipherType = WLAN_CIPHER_SUITE_CCMP; + switch (priv->cipherType) { + + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WEP\n"); + encrypthdr = WEP_ENCRYPT_HDR_SIZE; + encrypttailsize = WEP_ENCRYPT_TAIL_SIZE; + encrypt = 1; + EncrType = 1; + break; + + case WLAN_CIPHER_SUITE_TKIP: + bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WPA\n"); + encrypthdr = WPA_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA_ENCRYPT_TAIL_SIZE; + encrypt = 1; + EncrType = 3; + break; + + case WLAN_CIPHER_SUITE_CCMP: + bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WPA2\n"); + encrypthdr = WPA2_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE; + encrypt = 1; + EncrType = 5; + break; + + case WLAN_CIPHER_SUITE_SMS4: + bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WAPI\n"); + encrypthdr = WAPI_ENCRYPT_HDR_SIZE; + encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE; + encrypt = 1; + EncrType = 7; + break; + + default: + encrypthdr = 0; + encrypttailsize = 0; + encrypt = 0; + EncrType = 8; + break; + } + + framehdrlen += encrypthdr; + + /* Filling the 802.11 Hdr */ + dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + if (priv->vif->type == NL80211_IFTYPE_STATION) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS); + else + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + + if (encrypt) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP); + + if (priv->vif->bss_conf.qos) { + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] QOS Enabled\n"); + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP); + *(u16 *)(dot11hdr + 1) = 0x0; + framehdrlen += 2; + } else { + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA); + } + + memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); + memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(dot11hdr->addr3, iac->bd.dest_mac, ETH_ALEN); + + /* Filling the LLC/SNAP Hdr */ + snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen); + memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \ + sizeof(*snaphdr)); + *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IP); + /* Updating the framebdylen with snaphdr and LLC hdr size */ + framebdylen = sizeof(*snaphdr) + 2; + + /* proto == 0 for udp stream or 1 for tcp stream; */ + (iac->bd.proto) ? (tu_len = sizeof(struct tcp_header)):(tu_len = sizeof(struct udp_header)); + (iac->bd.proto) ? (tu_proto = TCP_PROTO):(tu_proto = UDP_PROTO); + + /* Filling the ip header */ + ptr = (u8 *)dot11hdr + framehdrlen + framebdylen; + ipheader = (struct ip_header *)kzalloc(128, GFP_ATOMIC); + ipheader->_v_hl = 0x45; + ipheader->_tos = 0x00; + ipheader->_len = __swab16(sizeof(*ipheader) + tu_len + bodylen); + ipheader->_id = 0; + ipheader->_offset = 0; + ipheader->_ttl = 0xff; + ipheader->_proto = tu_proto; + ipheader->_chksum = 0; + ipheader->src = __swab32(iac->iphd.src); + ipheader->dest = __swab32(iac->iphd.dest); + + /* Calculate ip header's checksum value; */ + ipheader->_chksum = (csum((uint16_t *)ipheader, sizeof(*ipheader))); + + /* Updating the frmbdylen with ip Hdr */ + framebdylen += sizeof(*ipheader); + + if (tu_proto == TCP_PROTO) { + /* Filling the tcp header */ + tcpheader = (struct tcp_header *)((u8 *)ipheader + sizeof(struct ip_header)); + tcpheader->src = __swab16(iac->tcphd.src); + tcpheader->dest = __swab16(iac->tcphd.dest); + tcpheader->seqno = __swab32(iac->bd.next_seqno); + tcpheader->ackno = __swab32(iac->tcphd.ackno); + tcpheader->_hdrlen_rsvd_flags = 0x1050; + tcpheader->wnd = 0xFFFF; + tcpheader->chksum = 0; + tcpheader->urgp = 0; + + /* Updating the frmbdylen with tcp Hdr */ + framebdylen += sizeof(*tcpheader); + + /* Filling the tcp body */ + tcpbody = (u8 *)dot11hdr + framehdrlen + framebdylen; + memcpy(tcpbody, payload, bodylen); + + /* Calculate tcp's checksum value; */ + tcpcsum(ipheader, tcpheader, payload, bodylen); + } else if (tu_proto == UDP_PROTO) { + /* Filling the udp header */ + udpheader = (struct udp_header *)((u8 *)ipheader + sizeof(struct ip_header)); + udpheader->src = __swab16(iac->udphd.src); + udpheader->dest = __swab16(iac->udphd.dest); + udpheader->len = __swab16(sizeof(struct udp_header) + bodylen); + udpheader->chksum = 0; + + /* Updating the frmbdylen with udp Hdr */ + framebdylen += sizeof(*udpheader); + + /* Filling the tcp body */ + udpbody = (u8 *)dot11hdr + framehdrlen + framebdylen; + memcpy(udpbody, payload, bodylen); + } + + memcpy(ptr, ipheader, sizeof(struct ip_header) + tu_len); + kfree(ipheader); + + /* Updating the frmbdylen with tcp payload */ + framebdylen += bodylen; + + /* Updating the framebdylen with Encryption Tail Size */ + framebdylen += encrypttailsize; + + /* Filling the Template Frame Hdr. + * Bit7 of the first idx stards for tcp/udp protocol,0 for udp and 1 for tcp; + * Bit6 indicates if send the heartbeat or not. 1 to send and 0 not. + * Bits 3-5 reserve for use. + * Bit 0-2 stands for stream index. + */ + if (iac->klv_vendor == KLV_VENDOR_DEFAULT) { + tmp_frame[0] = idx | (1 << 6) | (iac->bd.proto << 7); + } else { + tmp_frame[0] = idx | (1 << 6) | (iac->bd.proto << 7); + } + tmp_frame[1] = 0xFF; /* Fixed to 0xFF */ + ((u16 *)&tmp_frame[2])[0] = framehdrlen + framebdylen + AES_KEY_IV_LEN + 1; + memcpy(&tmp_frame[framehdrlen + framebdylen + 4], iac->aes_key, AES_KEY_LEN); + memcpy(&tmp_frame[framehdrlen + framebdylen + 20], iac->aes_iv, AES_IV_LEN); + /* + *bit0-bit3 stands for encrypt type, + *bit4-bit7 stands for klv_vendor; + */ + tmp_frame[framehdrlen + framebdylen + 4 + AES_KEY_IV_LEN] = (EncrType | (iac->klv_vendor << 4)); + + ret = WARN_ON(wsm_write_mib(hw_priv, + WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME, + tmp_frame, + (framehdrlen + framebdylen + 4 + AES_KEY_IV_LEN + 1), + priv->if_id)); + kfree(tmp_frame); + +exit_p: + return ret; +} + +int bes2600_del_ip_offload(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + u8 stream_idx) +{ + u8 tmp_frame[8]; + int ret; + + /* Filling the Template Frame Hdr */ + tmp_frame[0] = stream_idx; + tmp_frame[1] = 0xFF; /* Fixed to 0xFF */ + ((u16 *)&tmp_frame[2])[0] = 0; + + ret = WARN_ON(wsm_write_mib(hw_priv, + WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME, + tmp_frame, + 4, + priv->if_id)); + + return ret; +} + +int bes2600_en_ip_offload(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + u16 period_in_s) +{ + int ret; + struct MIB_TCP_KEEP_ALIVE_PERIOD period; + u8 EncrType; + + if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) + priv->cipherType = WLAN_CIPHER_SUITE_CCMP; + switch (priv->cipherType) { + + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + EncrType = 1; + break; + + case WLAN_CIPHER_SUITE_TKIP: + EncrType = 3; + break; + + case WLAN_CIPHER_SUITE_CCMP: + EncrType = 5; + break; + + case WLAN_CIPHER_SUITE_SMS4: + EncrType = 7; + break; + + default: + EncrType = 8; + break; + } + + /* Send mib WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD */ + period.TcpKeepAlivePeriod = period_in_s; + period.EncrType = EncrType; + period.Reserved = 0; + ret = WARN_ON(wsm_write_mib(hw_priv, + WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD, + (u8 *)&period, + sizeof(period), + priv->if_id)); + + return ret; +} + +static int search_for_free_stream(struct bes2600_common *hw_priv, uint8_t proto, + uint16_t src_port, uint16_t dst_port, + uint32_t src_ip, uint32_t dst_ip) +{ + int i = 0, free_idx = -1; + struct ip_alive_cfg *iac = hw_priv->iac; + + /* If port matches, overwrites the configuration; */ + for (i = 0; i < NUM_IP_FRAMES; i++) { + if (iac[i].bd.idx_used == 1 && + iac[i].bd.proto == proto && + iac[i].bd.src_port == src_port && + iac[i].bd.dest_port == dst_port && + iac[i].bd.src_ip == src_ip && + iac[i].bd.dest_ip == dst_ip) { + free_idx = i; + break; + } else if (free_idx == -1 && iac[i].bd.idx_used == 0) { + free_idx = i; + iac[i].bd.idx_used = 1; + } + } + + return free_idx; +} + +static int get_keep_alive_used_stream(struct ieee80211_hw *hw) +{ + int i; + struct ip_alive_cfg *iac = ((struct bes2600_common *)hw->priv)->iac; + u8 stream = 0; + + for (i = 0; i < NUM_IP_FRAMES; i++) { + if (iac[i].bd.idx_used == 1) + stream |= (1 << i); + } + return bes2600_testmode_reply(hw->wiphy, &stream, sizeof(u8)); +} + +#ifdef VENDOR_XM_KEEPALIVE +void bes2600_get_keepalive_info(struct bes2600_common *hw_priv, struct ip_alive_satus *status) +{ + int i; + struct ip_alive_cfg *iac = hw_priv->iac; + + status->udp = false; + status->tcp = false; + + for (i = 0; i < NUM_IP_FRAMES; i++) { + if (iac[i].bd.idx_used == 1) { + if (iac[i].bd.proto == 0) + status->udp = true; + else if (iac[i].bd.proto == 1) + status->tcp = true; + } + } +} +#endif + +static int net_device_add_ip_offload(struct ieee80211_hw *hw, u8 *data, int len) +{ + struct ip_alive_paras *paras = (struct ip_alive_paras *)data; + struct ip_alive_cfg *iac = ((struct bes2600_common *)hw->priv)->iac; + int idx_check; + struct ip_stream_cfg idx_cfg = { + .err_code = 0, + .idx = -1, + }; + + /* only allowed paras->idx == 0xF */ + if (paras->idx != 0xF) { + idx_cfg.err_code = -1; /* cmd with idx err */ + bes2600_info(BES2600_DBG_TEST_MODE, "cmd with idx err\n"); + goto err_idx; + } + + if (paras->payload_len <= IP_KEEPALIVE_MAX_LEN) { + /* idx == 0xF, find a free one. */ + idx_check = search_for_free_stream(hw->priv, paras->proto, + paras->src_port, + paras->dst_port, + paras->src_ip, + paras->dst_ip); + if (idx_check < 0 || idx_check >= NUM_IP_FRAMES) { + paras->idx = 0xF; + idx_cfg.err_code = -2; /* get stream fail */ + bes2600_info(BES2600_DBG_TEST_MODE, "Exceeded maximum number of keepalive streams\n"); + goto err_idx; + } else { + paras->idx = idx_check; + } + + iac[paras->idx].bd.proto = paras->proto; + iac[paras->idx].klv_vendor = paras->klv_vendor; + iac[paras->idx].bd.src_port = paras->src_port; + iac[paras->idx].bd.dest_port = paras->dst_port; + iac[paras->idx].iphd.src = iac[paras->idx].bd.src_ip = paras->src_ip; + iac[paras->idx].iphd.dest = iac[paras->idx].bd.dest_ip = paras->dst_ip; + if (paras->proto) { //tcp + iac[paras->idx].tcphd.src = paras->src_port; + iac[paras->idx].tcphd.dest = paras->dst_port; + } else { //udp + iac[paras->idx].udphd.src = paras->src_port; + iac[paras->idx].udphd.dest = paras->dst_port; + } + + if (iac[paras->idx].klv_vendor == KLV_VENDOR_DEFAULT) { + iac[paras->idx].bd.len = paras->payload_len; + } else if (iac[paras->idx].klv_vendor == KLV_VENDOR_XM) { + iac[paras->idx].bd.len = paras->payload_len; + if (paras->proto) { //tcp + //Reserve space for pkcs5 padding and base64 coding in firmware; + iac[paras->idx].bd.len = (iac[paras->idx].bd.len + (AES_KEY_LEN - + (iac[paras->idx].bd.len % AES_KEY_LEN))) * 8 / 6; + //Reserve space for websocket frame's head, + iac[paras->idx].bd.len += WEBSOCKET_HD_LEN; + } + } + + memset(iac[paras->idx].bd.payload, 0, IP_KEEPALIVE_MAX_LEN); + memcpy(iac[paras->idx].bd.payload, paras->payload, paras->payload_len); + memcpy(iac[paras->idx].aes_key, paras->key, AES_KEY_LEN); + memcpy(iac[paras->idx].aes_iv, paras->iv, AES_IV_LEN); + + bes2600_info(BES2600_DBG_TEST_MODE, "idx = %d, len=%d\n", paras->idx, iac[paras->idx].bd.len); + idx_cfg.idx = paras->idx; + } else { + bes2600_err(BES2600_DBG_TEST_MODE, "payload_len = %d, too long!\n", paras->payload_len); + idx_cfg.err_code = -3; /* playload err */ + } + +err_idx: + return bes2600_testmode_reply(hw->wiphy, &idx_cfg, sizeof(struct ip_stream_cfg)); + +} + +static int net_device_del_ip_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len) +{ + struct ip_alive_iac_idx *alive_iac_idx = (struct ip_alive_iac_idx *)data; + int idx = alive_iac_idx->idx; + struct bes2600_common *hw_priv = hw->priv; + struct ip_alive_cfg *iac = hw_priv->iac; + struct bes2600_vif *priv; + + if (vif) { + priv = cw12xx_get_vif_from_ieee80211(vif); + } else { + bes2600_err(BES2600_DBG_TEST_MODE, "failed to get vif\n"); + return -EOPNOTSUPP; + } + + if (idx <= NUM_IP_FRAMES && idx >= 0) { + iac[idx].bd.idx_used = 0; + bes2600_del_ip_offload(hw_priv, priv, idx); + bes2600_info(BES2600_DBG_TEST_MODE, "delete idx = %d\n", idx); + } else { + bes2600_err(BES2600_DBG_TEST_MODE, "wrong idx = %d!\n", idx); + idx = -1; /* invalid idx */ + } + return bes2600_testmode_reply(hw->wiphy, &idx, sizeof(int)); +} + + +static int net_device_en_ip_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len) +{ + struct ip_alive_period *alive_period = (struct ip_alive_period *)data; + int period = alive_period->period; + struct bes2600_common *hw_priv = hw->priv; + struct ip_alive_cfg *iac = hw_priv->iac; + struct bes2600_vif *priv; + u8 idx; + u8 ip_ack_filter[10] = { + 1, // action mode: 0 disable, 1 filter out, 2 filter in + 1, // filter number + 1, // filter_mode: 0 disable, 1 filter out, 2 filter in + 3, // address_mode: 1, src addr, 2 dest addr, 3 tcp ack + }; + + if (vif) { + priv = cw12xx_get_vif_from_ieee80211(vif); + } else { + bes2600_err(BES2600_DBG_TEST_MODE, "failed to get vif\n"); + return -EOPNOTSUPP; + } + + for (idx = 0; idx < NUM_IP_FRAMES; idx++) { + if (iac[idx].bd.idx_used) { + bes2600_set_ip_offload(hw_priv, priv, &iac[idx], idx); + bes2600_set_ipv4addrfilter(hw_priv, ip_ack_filter, priv->if_id); + } + } + + bes2600_en_ip_offload(hw_priv, priv, period); + bes2600_info(BES2600_DBG_TEST_MODE, "en period = %d\n", period); + + return 0; +} +#endif /* CONFIG_BES2600_KEEP_ALIVE */ + +#ifdef PLAT_ALLWINNER_R329 +static int bes2600_factory_cali_to_flash(struct ieee80211_hw *hw) +{ + struct bes2600_common *hw_priv = hw->priv; + u8 *factory_data = NULL; + u8 *file_buffer = NULL; + u32 factory_data_len = 0; + int ret; + + if (bes2600_chrdev_is_signal_mode()) + return -EOPNOTSUPP; + + if (!(file_buffer = bes2600_factory_get_file_buffer())) + return -ENOMEM; + + bes2600_factory_lock(); + factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH); + if (!factory_data) { + bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n"); + ret = -ENOENT; + } else { + bes2600_factory_data_check(factory_data); + factory_little_endian_cvrt(factory_data); + ret = wsm_save_factory_txt_to_flash(hw_priv, factory_data, 0); + bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "save factory data to flash failed.\n"); + } + bes2600_factory_free_file_buffer(file_buffer); + bes2600_factory_unlock(); + + if (ret >= 0) + return bes2600_testmode_reply(hw->wiphy, &ret, sizeof(int)); + + return ret; + +} +#endif + +static int bes2600_vendor_cpu_usage(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + return wsm_cpu_usage_cmd(hw->priv); +} + +static int bes2600_vendor_epta_parm_config(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len) +{ +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + struct bes2600_common *hw_priv = hw->priv; + struct vendor_epta_parm *epta_para = (struct vendor_epta_parm *)data; + + return coex_set_epta_params(hw_priv, epta_para->wlan_duration, + epta_para->bt_duration, epta_para->hw_epta_enable); +#else + return -EOPNOTSUPP; +#endif +} + +/** + * bes2600_testmode_cmd -called when tesmode command + * reaches bes2600 + * + * @hw: the hardware + * @data: incoming data + * @len: incoming data length + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) +{ + int ret = 0; + struct nlattr *type_p = nla_find(data, len, BES_TM_MSG_ID); + struct nlattr *data_p = nla_find(data, len, BES_TM_MSG_DATA); + + if (!type_p || !data_p) + return -EINVAL; + + bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: type: %i\n", + __func__, nla_get_u32(type_p)); + + switch (nla_get_u32(type_p)) { + case BES_MSG_TEST: + ret = bes2600_test(hw, + nla_data(data_p), nla_len(data_p)); + break; + case BES_MSG_SET_SNAP_FRAME: + ret = bes2600_set_snap_frame(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case BES_MSG_GET_TX_POWER_LEVEL: + ret = bes2600_get_tx_power_level(hw); + break; + case BES_MSG_GET_TX_POWER_RANGE: + ret = bes2600_get_tx_power_range(hw); + break; + case BES_MSG_SET_ADVANCE_SCAN_ELEMS: + ret = bes2600_set_advance_scan_elems(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case BES_MSG_SET_TX_QUEUE_PARAMS: + ret = bes2600_set_txqueue_params(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case BES_MSG_GET_TSM_PARAMS: + ret = bes2600_get_tsm_params(hw); + break; + case BES_MSG_START_STOP_TSM: + ret = bes2600_start_stop_tsm(hw, (u8 *) nla_data(data_p)); + break; + case BES_MSG_GET_ROAM_DELAY: + ret = bes2600_get_roam_delay(hw); + break; + case BES_MSG_SET_POWER_SAVE: + ret = bes2600_set_power_save(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case BES_MSG_ADD_IP_OFFLOAD: +#ifdef CONFIG_BES2600_KEEP_ALIVE + ret = net_device_add_ip_offload(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); +#else + ret = -EPERM; +#endif + break; + case BES_MSG_DEL_IP_OFFLOAD: +#ifdef CONFIG_BES2600_KEEP_ALIVE + ret = net_device_del_ip_offload(hw, vif, (u8 *) nla_data(data_p), + nla_len(data_p)); +#else + ret = -EPERM; +#endif + break; + case BES_MSG_SET_IP_OFFLOAD_PERIOD: +#ifdef CONFIG_BES2600_KEEP_ALIVE + ret = net_device_en_ip_offload(hw, vif, (u8 *) nla_data(data_p), + nla_len(data_p)); +#else + ret = -EPERM; +#endif + break; + case BES_MSG_GET_KEEP_ALIVE_STREAM: +#ifdef CONFIG_BES2600_KEEP_ALIVE + ret = get_keep_alive_used_stream(hw); +#else + ret = -EPERM; +#endif + break; + case BES_MSG_SAVE_CALI_TXT_TO_FLASH: +#ifdef PLAT_ALLWINNER_R329 + ret = bes2600_factory_cali_to_flash(hw); +#else + ret = -EPERM; +#endif + break; + case BES_MSG_VENDOR_RF_CMD: + ret = bes2600_vendor_rf_cmd(hw, vif, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case BES_MSG_EPTA_PARM_CONFIG: + ret = bes2600_vendor_epta_parm_config(hw, vif, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case BES_MSG_MCU_CPUUSAGE: + ret = bes2600_vendor_cpu_usage(hw, vif); + break; + default: + break; + } + return ret; +} +#endif /* CONFIG_BES2600_TESTMODE */ diff --git a/drivers/staging/bes2600/sta.h b/drivers/staging/bes2600/sta.h new file mode 100644 index 000000000000..f97e8fc1bb21 --- /dev/null +++ b/drivers/staging/bes2600/sta.h @@ -0,0 +1,146 @@ +/* + * Mac80211 STA interface for BES2600 mac80211 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +int bes2600_start(struct ieee80211_hw *dev); +void bes2600_stop(struct ieee80211_hw *dev); +int bes2600_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +void bes2600_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +int bes2600_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); + +int bes2600_config(struct ieee80211_hw *dev, u32 changed); +int bes2600_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); +void bes2600_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int bes2600_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +int bes2600_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +/* Not more a part of interface? +int bes2600_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats); +*/ +int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + +int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); + +int bes2600_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type); + +int bes2600_cancel_remain_on_channel(struct ieee80211_hw *hw +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + , struct ieee80211_vif *vif +#endif + ); + +int bes2600_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + +u64 bes2600_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); + +int bes2600_set_pm(struct bes2600_vif *priv, const struct wsm_set_pm *arg); + +void bes2600_set_data_filter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, + int len); + +u32 bes2600_bh_get_encry_hdr_len(u32 cipherType); +/* ******************************************************************** */ +/* WSM callbacks */ + +/* void bes2600_set_pm_complete_cb(struct bes2600_common *hw_priv, + struct wsm_set_pm_complete *arg); */ +void bes2600_channel_switch_cb(struct bes2600_common *hw_priv); + +/* ******************************************************************** */ +/* WSM events */ + +void bes2600_free_event_queue(struct bes2600_common *hw_priv); +void bes2600_event_handler(struct work_struct *work); +void bes2600_bss_loss_work(struct work_struct *work); +void bes2600_connection_loss_work(struct work_struct *work); +void bes2600_keep_alive_work(struct work_struct *work); +void bes2600_tx_failure_work(struct work_struct *work); +void bes2600_dynamic_opt_txrx_work(struct work_struct *work); + +/* ******************************************************************** */ +/* Internal API */ + +int bes2600_setup_mac(struct bes2600_common *hw_priv); +void bes2600_join_work(struct work_struct *work); +void bes2600_join_timeout(struct work_struct *work); +void bes2600_unjoin_work(struct work_struct *work); +void bes2600_offchannel_work(struct work_struct *work); +void bes2600_wep_key_work(struct work_struct *work); +void bes2600_update_filtering(struct bes2600_vif *priv); +void bes2600_update_filtering_work(struct work_struct *work); +int __bes2600_flush(struct bes2600_common *hw_priv, bool drop, int if_id); +void bes2600_set_beacon_wakeup_period_work(struct work_struct *work); +int bes2600_enable_listening(struct bes2600_vif *priv, + struct ieee80211_channel *chan); +int bes2600_disable_listening(struct bes2600_vif *priv); +int bes2600_set_uapsd_param(struct bes2600_vif *priv, + const struct wsm_edca_params *arg); +void bes2600_ba_work(struct work_struct *work); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) +void bes2600_ba_timer(struct timer_list *t); +#else +void bes2600_ba_timer(unsigned long arg); +#endif +const u8 *bes2600_get_ie(u8 *start, size_t len, u8 ie); +int bes2600_vif_setup(struct bes2600_vif *priv); +int bes2600_setup_mac_pvif(struct bes2600_vif *priv); +void bes2600_iterate_vifs(void *data, u8 *mac, + struct ieee80211_vif *vif); +void bes2600_rem_chan_timeout(struct work_struct *work); +int bes2600_set_macaddrfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data); +#ifdef IPV6_FILTERING +int bes2600_set_na(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +#endif /*IPV6_FILTERING*/ +#ifdef CONFIG_BES2600_TESTMODE +void bes2600_device_power_calc(struct bes2600_common *priv, + s16 max_output_power, s16 fe_cor, u32 band); +int bes2600_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len); +int bes2600_testmode_event(struct wiphy *wiphy, const u32 msg_id, + const void *data, int len, gfp_t gfp); +int bes2600_get_tx_power_range(struct ieee80211_hw *hw); +int bes2600_get_tx_power_level(struct ieee80211_hw *hw); +#endif /* CONFIG_BES2600_TESTMODE */ +#ifdef CONFIG_BES2600_WLAN_BES +int bes2600_wifi_start(struct bes2600_common *hw_priv); +int bes2600_wifi_stop(struct bes2600_common *hw_priv); +#endif +#endif /* STA_H_INCLUDED */ diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c new file mode 100644 index 000000000000..dbd5c37c6b9d --- /dev/null +++ b/drivers/staging/bes2600/tx_loop.c @@ -0,0 +1,530 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "bes2600.h" +#include "wsm.h" +#include "queue.h" + +struct tx_loop_table +{ + u16 cmd; + void (*proc)(struct bes2600_common *hw_priv, u8 *buf, u32 len); +}; + +static void bes2600_tx_loop_build_lmac_tx_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len); +static void bes2600_tx_loop_build_scan_compl_ind(struct bes2600_common *hw_priv); +static void bes2600_tx_loop_build_pm_ind(struct bes2600_common *hw_priv); + +void bes2600_tx_loop_init(struct bes2600_common *hw_priv) +{ + hw_priv->tx_loop.enabled = false; + hw_priv->tx_loop.start_lmac_seq = 0; + hw_priv->tx_loop.start_mcu_seq = 0; + spin_lock_init(&hw_priv->tx_loop.tx_loop_lock); + skb_queue_head_init(&hw_priv->tx_loop.rx_queue); +} + +struct sk_buff *bes2600_tx_loop_read(struct bes2600_common *hw_priv) +{ + struct sk_buff *skb; + struct wsm_hdr *wsm; + + if(hw_priv == NULL) + return NULL; + + skb = skb_dequeue(&hw_priv->tx_loop.rx_queue); + if(skb != NULL) { + wsm = (struct wsm_hdr *)skb->data; + bes2600_dbg(BES2600_DBG_TXLOOP, "tx loop pipe read msg_id:0x%04x seq:%d\n", + WSM_MSG_ID_GET(wsm->id), WSM_MSG_SEQ_GET(wsm->id)); + } + + return skb; +} + +static void bes2600_tx_loop_item_pending_item(struct bes2600_common *hw_priv, struct sk_buff *skb) +{ + bes2600_info(BES2600_DBG_TXLOOP, "tx loop confirm pending skb.\n"); + bes2600_tx_loop_build_lmac_tx_cfm(hw_priv, skb->data, skb->data_len); +} + +void bes2600_tx_loop_record_wsm_cmd(struct bes2600_common *hw_priv, u8 *wsm_cmd) +{ + hw_priv->tx_loop.wsm_cmd_ptr = wsm_cmd; +} + +void bes2600_tx_loop_clear_wsm_cmd(struct bes2600_common *hw_priv) +{ + hw_priv->tx_loop.wsm_cmd_ptr = NULL; +} + +void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv) +{ + int i = 0; + u16 cmd_id = -1; + + if(hw_priv == NULL) + return; + + if(hw_priv->tx_loop.enabled) + return; + + WARN_ON(1); + hw_priv->tx_loop.enabled = true; + hw_priv->tx_loop.start_lmac_seq = hw_priv->wsm_rx_seq[0]; + hw_priv->tx_loop.start_mcu_seq = hw_priv->wsm_rx_seq[1]; + + if(hw_priv->tx_loop.wsm_cmd_ptr) { + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)hw_priv->tx_loop.wsm_cmd_ptr; + cmd_id = tx_wsm->id & WSM_MSG_ID_MASK; + + if(cmd_id == 0x0005 || cmd_id == 0x0006){ + struct wsm_mib *mib = (struct wsm_mib *)hw_priv->wsm_cmd.arg; + u16 mib_id = mib->mibId; + bes2600_info(BES2600_DBG_TXLOOP, "pending cmd:0x%04x seq:%d mib_id:0x%04x\n", + cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id), mib_id); + } else { + bes2600_info(BES2600_DBG_TXLOOP, "pending cmd:0x%04x seq:%d\n", + cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id)); + } + + bes2600_tx_loop_pipe_send(hw_priv, hw_priv->tx_loop.wsm_cmd_ptr, 8); + } + + if (atomic_read(&hw_priv->scan.in_progress) && cmd_id != 0x0007) { + bes2600_tx_loop_build_scan_compl_ind(hw_priv); + } else if (atomic_read(&hw_priv->bes_power.pm_set_in_process) && cmd_id != 0x0010) { + bes2600_tx_loop_build_pm_ind(hw_priv); + } + + for (i = 0; i < 4; i++) { + bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i], + bes2600_tx_loop_item_pending_item); + } + + if (atomic_read(&hw_priv->bh_rx) > 0) + wake_up(&hw_priv->bh_wq); + +} + +static void bes2600_tx_loop_build_lmac_generic_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm, *tx_wsm; + u32 msg_len = sizeof(struct wsm_hdr) + 4; + u16 msg_id = 0; + + tx_wsm = (struct wsm_hdr *)buf; + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= 0x400; // set confirm flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + *((u32 *)&rx_wsm[1]) = __cpu_to_le32(WSM_STATUS_SUCCESS); + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_lmac_tx_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm; + struct wsm_tx *tx_wsm; + struct wsm_tx_confirm *cfm; + u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_tx_confirm); + u16 msg_id = 0; + + tx_wsm = (struct wsm_tx *)buf; + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->hdr.id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id = (tx_wsm->hdr.id & WSM_MSG_ID_MASK); + msg_id |= 0x400; // set confirm flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + + cfm = (struct wsm_tx_confirm *)&rx_wsm[1]; + cfm->packetID = tx_wsm->packetID; + cfm->status = WSM_STATUS_SUCCESS; + cfm->txedRate = tx_wsm->maxTxRate; + cfm->ackFailures = 0; + cfm->flags = WSM_TX_STATUS_NORMAL_ACK; + cfm->txQueueDelay = 5; + cfm->mediaDelay = 3; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_config_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm; + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_configuration) + 4; + u16 msg_id = 0; + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= 0x400; // set confirm flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + *(u32 *)&rx_wsm[1] = WSM_STATUS_SUCCESS; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_read_mib_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm; + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + struct wsm_mib *mib = (struct wsm_mib *)hw_priv->wsm_cmd.arg; + u32 *status; + u16 *mib_id, *size; + u32 msg_len = sizeof(struct wsm_hdr) + 4 /* status */ + 2 /* mib_id */ + mib->buf_size; + u16 msg_id = 0; + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= 0x400; // set confirm flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + + status = (u32 *)&rx_wsm[1]; + *status = WSM_STATUS_SUCCESS; + + mib_id = (u16 *)&status[1]; + *mib_id = mib->mibId; + + size = (u16 *)&mib_id[1]; + *size = mib->buf_size; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_join_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm; + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + u16 msg_id = 0; + u32 msg_len = sizeof(struct wsm_hdr) + 4 /* status */ + 8 /* power_level */; + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= 0x400; // set confirm flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + *(u32 *)&rx_wsm[1] = WSM_STATUS_SUCCESS; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_rfcmd_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_mcu_hdr *rx_wsm; + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + u16 msg_id = 0; + u32 msg_len = sizeof(struct wsm_mcu_hdr); + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_mcu_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq); + rx_wsm->hdr.id = __cpu_to_le16(msg_id); + rx_wsm->hdr.len = __cpu_to_le16(msg_len); + rx_wsm->handle_label = WSM_TO_MCU_CMD_CONFIRM_LABEL; + rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK); + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_driver_cmd_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_mcu_hdr *rx_wsm; + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + u16 msg_id = 0; + u32 msg_len = sizeof(struct wsm_mcu_hdr) + 4; + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n", + __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_mcu_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq); + rx_wsm->hdr.id = __cpu_to_le16(msg_id); + rx_wsm->hdr.len = __cpu_to_le16(msg_len); + rx_wsm->handle_label = WSM_TO_MCU_CMD_CONFIRM_LABEL; + rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK); + *(uint32_t *)(rx_wsm + 1) = 0; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static struct tx_loop_table tx_loop_tbl[] = { + {.cmd = 0x0004, .proc = bes2600_tx_loop_build_lmac_tx_cfm}, + {.cmd = 0x0009, .proc = bes2600_tx_loop_build_config_cfm}, + {.cmd = 0x0005, .proc = bes2600_tx_loop_build_read_mib_cfm}, + {.cmd = 0x0006, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x000B, .proc = bes2600_tx_loop_build_join_cfm}, + {.cmd = 0x0007, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0008, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x000A, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x000C, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x000D, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0010, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0011, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0012, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0013, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0016, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0017, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0018, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0019, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x001A, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x001B, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x001C, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0029, .proc = bes2600_tx_loop_build_lmac_generic_cfm}, + {.cmd = 0x0C25, .proc = bes2600_tx_loop_build_rfcmd_cfm}, + {.cmd = 0x0C27, .proc = bes2600_tx_loop_build_driver_cmd_cfm}, +}; + +static void bes2600_tx_loop_build_scan_compl_ind(struct bes2600_common *hw_priv) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm; + struct wsm_scan_complete *scan_compl; + u16 msg_id = 0; + u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_scan_complete); + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_info(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s len:%d\n", __func__, msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id |= 0x806; // set indication flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + + scan_compl = (struct wsm_scan_complete *)&rx_wsm[1]; + scan_compl->status = WSM_STATUS_SUCCESS; + scan_compl->psm = WSM_PSM_PS; + scan_compl->numChannels = 2; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_pm_ind(struct bes2600_common *hw_priv) +{ + struct sk_buff *out_skb; + struct wsm_hdr *rx_wsm; + struct wsm_set_pm_complete *pm_compl; + u16 msg_id = 0; + u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_set_pm_complete); + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_info(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s len:%d\n", __func__, msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_hdr *)out_skb->data; + msg_id |= 0x809; // set indication flag + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq); + rx_wsm->id = __cpu_to_le16(msg_id); + rx_wsm->len = __cpu_to_le16(msg_len); + + pm_compl = (struct wsm_set_pm_complete *)&rx_wsm[1]; + pm_compl->status = WSM_STATUS_SUCCESS; + pm_compl->psm = WSM_PSM_PS; + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +static void bes2600_tx_loop_build_rfcmd_ind(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + struct sk_buff *out_skb; + struct wsm_mcu_hdr *rx_wsm; + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + u16 msg_id = 0; + u32 msg_len = sizeof(struct wsm_mcu_hdr); + + out_skb = dev_alloc_skb(msg_len); + if(IS_ERR_OR_NULL(out_skb)) { + bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__); + return; + } + + bes2600_dbg(BES2600_DBG_TXLOOP, "%s len:%d\n",__func__, msg_len); + skb_put(out_skb, msg_len); + rx_wsm = (struct wsm_mcu_hdr *)out_skb->data; + msg_id = (tx_wsm->id & WSM_MSG_ID_MASK); + msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq); + rx_wsm->hdr.id = __cpu_to_le16(msg_id); + rx_wsm->hdr.len = __cpu_to_le16(msg_len); + rx_wsm->handle_label = WSM_TO_MCU_CMD_INDICATION_LABEL; + rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK); + + spin_lock(&hw_priv->tx_loop.tx_loop_lock); + hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7; + skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb); + spin_unlock(&hw_priv->tx_loop.tx_loop_lock); + + atomic_add_return(1, &hw_priv->bh_rx); +} + +void bes2600_tx_loop_pipe_send(struct bes2600_common *hw_priv, u8 *buf, u32 len) +{ + int i = 0; + int tbl_size = ARRAY_SIZE(tx_loop_tbl); + struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf; + u16 cmd_id = tx_wsm->id & WSM_MSG_ID_MASK; + + /* don't need to tx loop if wifi is unregistered */ + if(hw_priv == NULL) + return; + + bes2600_dbg(BES2600_DBG_TXLOOP, "tx loop pipe send cmd:0x%04x seq:%d\n", + cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id)); + + /* select build confirm function based on command id */ + for(i = 0; i < tbl_size; i++) { + if(cmd_id == tx_loop_tbl[i].cmd) { + tx_loop_tbl[i].proc(hw_priv, buf, len); + break; + } + } + + /* build indication for special command */ + if(cmd_id == 0x0007) { + bes2600_tx_loop_build_scan_compl_ind(hw_priv); + } else if(cmd_id == 0x0010) { + bes2600_tx_loop_build_pm_ind(hw_priv); + } else if(cmd_id == 0x0C25) { + bes2600_tx_loop_build_rfcmd_ind(hw_priv, buf, len); + } +} \ No newline at end of file diff --git a/drivers/staging/bes2600/tx_loop.h b/drivers/staging/bes2600/tx_loop.h new file mode 100644 index 000000000000..7294025d8eca --- /dev/null +++ b/drivers/staging/bes2600/tx_loop.h @@ -0,0 +1,33 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __TX_LOOP_H__ +#define __TX_LOOP_H__ + +#include "bes2600.h" + +struct bes2600_tx_loop +{ + bool enabled; + spinlock_t tx_loop_lock; + u8 start_lmac_seq; + u8 start_mcu_seq; + struct sk_buff_head rx_queue; + u8 *wsm_cmd_ptr; +}; + +void bes2600_tx_loop_init(struct bes2600_common *hw_priv); +void bes2600_tx_loop_record_wsm_cmd(struct bes2600_common *hw_priv, u8 *wsm_cmd); +void bes2600_tx_loop_clear_wsm_cmd(struct bes2600_common *hw_priv); +struct sk_buff *bes2600_tx_loop_read(struct bes2600_common *hw_priv); +void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv); +void bes2600_tx_loop_pipe_send(struct bes2600_common *hw_priv, u8 *buf, u32 len); + +#endif \ No newline at end of file diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c new file mode 100644 index 000000000000..d9202bce7116 --- /dev/null +++ b/drivers/staging/bes2600/txrx.c @@ -0,0 +1,1995 @@ +/* + * Datapath implementation for BES2600 mac80211 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "bes2600.h" +#include "wsm.h" +#include "bh.h" +#include "ap.h" +#include "debug.h" +#include "sta.h" +#include "sbus.h" +#include "txrx_opt.h" + +#define BES2600_INVALID_RATE_ID (0xFF) + +#ifdef CONFIG_BES2600_TESTMODE +#include "bes_nl80211_testmode_msg.h" +#endif /* CONFIG_BES2600_TESTMODE */ +static const struct ieee80211_rate * +bes2600_get_tx_rate(const struct bes2600_common *hw_priv, + const struct ieee80211_tx_rate *rate); + +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] " + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void bes2600_check_go_neg_conf_success(struct bes2600_common *hw_priv, + u8 *action) +{ + if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A && + action[5] == 0x09 && action[6] == 0x02) { + if(action[17] == 0) { + hw_priv->is_go_thru_go_neg = true; + } + else { + hw_priv->is_go_thru_go_neg = false; + } + } +} + +static void bes2600_check_prov_desc_req(struct bes2600_common *hw_priv, + u8 *action) +{ + if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A && + action[5] == 0x09 && action[6] == 0x07) { + hw_priv->is_go_thru_go_neg = false; + } +} + +static void tx_policy_build(const struct bes2600_common *hw_priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i, j; + unsigned limit = hw_priv->short_frame_max_tx_count; + unsigned total = 0; + static int tx_rate_idx; + + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + + /* minstrel is buggy a little bit, so distille + * incoming rates first. */ + /* Sort rates in descending order. */ + for (i = 0; i < count; i++) { + if (rates[i].idx < 0) { + count = i; + break; + } + for (j= 0; j < count; j++) { + if (rates[j].idx < 0) { + continue; + } + + if (rates[j].idx < rates[j + 1].idx) { + struct ieee80211_tx_rate tmp = rates[j]; + rates[j] = rates[j + 1]; + rates[j + 1] = tmp; + } + } + } + /* enhance throughput, more tx retry rate */ +#ifdef BES2600_TX_RX_OPT + if (rates[0].flags & IEEE80211_TX_RC_MCS) { + static int min_rate_index = 1; //min rate index is mcs1 + static u8 last_rate_tx_cnt = 7; + + count = IEEE80211_TX_MAX_RATES; + /* if idx < min rate index, set min rate index */ + rates[0].count = 6; + if (rates[0].idx <= min_rate_index) { + rates[0].idx = min_rate_index; + } + + for (i = 1; i < count; ++i) { + /* only one rate try 2 times*/ + if (rates[i].count > 6) + rates[i].count = 6; + if (rates[i - 1].idx > min_rate_index) { + rates[i].idx = (rates[i - 1].idx - 1); + rates[i].count = 6; + rates[i].flags = rates[i - 1].flags; + } else if (rates[i - 1].idx <= min_rate_index) { + rates[i].idx = -1; + rates[i].count = 0; + rates[i].flags = 0; + break; + } + } + /* update the last rate index tx cnt */ + rates[i - 1].count = last_rate_tx_cnt; + count = i; + } +#endif + /* Eliminate duplicates. */ + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + count = i + 1; + + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. */ + if (limit < count) + limit = count; + if (total > limit) { + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; + limit -= rates[i].count; + } + } + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + /* ">> 1" is an equivalent of "/ 2", but faster */ + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + policy->defined = bes2600_get_tx_rate(hw_priv, &rates[0])->hw_value + 1; +#if 1 //add min basic rate in the tx path, driver should set wifi_Hook_cfg->new_run_flag |= RETRY_1M_RATE; + if (rates[0].flags & IEEE80211_TX_RC_MCS) { + int low_rate_idx = 0; /* set default 11b 1M */ + int low_rate_count = 1; + register unsigned rateid, off, shift, retries; + /* if the mcs < mcs3, more retry 2 times with low rate. */ + if (rates[0].idx < 3) { + low_rate_count += 2; + } + /* if the tx rate is mcs0, more retry 2 times with low rate. + * sample tx rate count is 1 in the minstrel_ht_set_rate.*/ + if (rates[0].idx == 0 || (rates[1].idx == 0 && rates[0].count == 1)) { + low_rate_count += 2; + /* smaple and mcs0 tx rate count set 2. */ + for (i = 0; i < count; i++) { + rates[0].count = (i + 1); + } + } + if (hw_priv->channel != NULL && hw_priv->channel->hw_value > 14) + low_rate_idx = 6; /* set default 11a 6M */ + + rateid = low_rate_idx; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + retries = low_rate_count; + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } +#endif + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + rateid = bes2600_get_tx_rate(hw_priv, &rates[i])->hw_value; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (unlikely(retries > 0x0F)) + rates[i].count = retries = 0x0F; + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } + + + if (rates[0].idx != tx_rate_idx) { + tx_rate_idx = rates[0].idx; + bes2600_dbg(BES2600_DBG_TXRX_OPT, "[TX policy] Policy (%lu): " \ + "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + (long unsigned)count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count, + rates[4].idx, rates[4].count); + } +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. */ + struct tx_policy_cache_entry *it; + /* Search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct bes2600_common *hw_priv) +{ + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + int i; + + bes2600_dbg(BES2600_DBG_TXRX_OPT, "tx_policy_init\n\r"); + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +void tx_policy_deinit(struct bes2600_common *hw_priv) +{ + bes2600_dbg(BES2600_DBG_TXRX_OPT, "tx_policy_deinit\n\r"); +} + +static int tx_policy_get(struct bes2600_common *hw_priv, + struct ieee80211_tx_rate *rates, + size_t count, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + struct tx_policy wanted; + + tx_policy_build(hw_priv, &wanted, rates, count); + + spin_lock_bh(&cache->lock); + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return BES2600_INVALID_RATE_ID; + } + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] Used TX policy: %d\n", + idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list */ + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] New TX policy: %d\n", + idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (unlikely(list_empty(&cache->free))) { + /* Lock TX queues. */ + bes2600_tx_queues_lock(hw_priv); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void tx_policy_put(struct bes2600_common *hw_priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (unlikely(locked) && !usage) { + /* Unlock TX queues. */ + bes2600_tx_queues_unlock(hw_priv); + } + spin_unlock_bh(&cache->lock); +} + +/* +bool tx_policy_cache_full(struct bes2600_common *hw_priv) +{ + bool ret; + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + spin_lock_bh(&cache->lock); + ret = list_empty(&cache->free); + spin_unlock_bh(&cache->lock); + return ret; +} +*/ + +static int tx_policy_upload(struct bes2600_common *hw_priv) +{ + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .hdr = { + .numTxRatePolicies = 0, + } + }; + int if_id = 0; + spin_lock_bh(&cache->lock); + + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_set_tx_rate_retry_policy_policy *dst = + &arg.tbl[arg.hdr.numTxRatePolicies]; + dst->policyIndex = i; + dst->shortRetryCount = + hw_priv->short_frame_max_tx_count; + dst->longRetryCount = hw_priv->long_frame_max_tx_count; + + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt */ + dst->policyFlags = BIT(2) | BIT(3); + + memcpy(dst->rateCountIndices, src->tbl, + sizeof(dst->rateCountIndices)); + src->uploaded = 1; + ++arg.hdr.numTxRatePolicies; + } + } + spin_unlock_bh(&cache->lock); + //bes2600_debug_tx_cache_miss(hw_priv); + bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] Upload %d policies\n", + arg.hdr.numTxRatePolicies); + /*TODO: COMBO*/ + return wsm_set_tx_rate_retry_policy(hw_priv, &arg, if_id); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct bes2600_common *hw_priv = + container_of(work, struct bes2600_common, tx_policy_upload_work); + + bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX] TX policy upload.\n"); + WARN_ON(tx_policy_upload(hw_priv)); + + wsm_unlock_tx(hw_priv); + bes2600_tx_queues_unlock(hw_priv); +} + +/* ******************************************************************** */ +/* bes2600 TX implementation */ + +struct bes2600_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct bes2600_sta_priv *sta_priv; + struct ieee80211_sta *sta; + struct bes2600_txpriv txpriv; +}; + +u32 bes2600_rate_mask_to_wsm(struct bes2600_common *hw_priv, u32 rates) +{ + u32 ret = 0; + int i; + struct ieee80211_rate * bitrates = + hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates; + for (i = 0; i < 32; ++i) { + if (rates & BIT(i)) + ret |= BIT(bitrates[i].hw_value); + } + return ret; +} + +static const struct ieee80211_rate * +bes2600_get_tx_rate(const struct bes2600_common *hw_priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &hw_priv->mcs_rates[rate->idx]; + return &hw_priv->hw->wiphy->bands[hw_priv->channel->band]-> + bitrates[rate->idx]; +} + +static int +bes2600_tx_h_calc_link_ids(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ +#ifndef P2P_MULTIVIF + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + if ((t->tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || + (hw_priv->roc_if_id == priv->if_id)) + t->txpriv.offchannel_if_id = 2; + else + t->txpriv.offchannel_if_id = 0; +#endif + + if (likely(t->sta && t->sta_priv->link_id)) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = priv->link_id_after_dtim; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; + } + } else { + t->txpriv.link_id = + bes2600_find_link_id(priv, t->da); + /* Do not assign valid link id for deauth/disassoc frame being + transmitted to an unassociated STA */ + if (!(t->txpriv.link_id) && + (ieee80211_is_deauth(t->hdr->frame_control) || + ieee80211_is_disassoc(t->hdr->frame_control))) { + t->txpriv.link_id = 0; + } else { + if (!t->txpriv.link_id) + t->txpriv.link_id = bes2600_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + wiphy_err(priv->hw->wiphy, + "%s: No more link IDs available.\n", + __func__); + return -ENOENT; + } + } + t->txpriv.raw_link_id = t->txpriv.link_id; + } + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + if (t->tx_info->sta && + (t->tx_info->sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = priv->link_id_uapsd; +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + return 0; +} + +static void +bes2600_tx_h_pm(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); + } +} + +static void +bes2600_tx_h_calc_tid(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} + +#ifdef CONFIG_BES2600_WAPI_SUPPORT +static void bes2600_tx_wapi_shrink_iv_space(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + int hdrlen; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) t->skb->data; + u8 *pos, iv_len; + + if (!t->tx_info->control.hw_key || + !ieee80211_has_protected(t->hdr->frame_control)) + return; + + iv_len = t->tx_info->control.hw_key->iv_len; + hdrlen = ieee80211_hdrlen(t->hdr->frame_control); + iv_len = t->tx_info->control.hw_key->iv_len; + + if(ieee80211_is_mgmt(t->hdr->frame_control) && + t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_SMS4 && + (t->tx_info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { + pos = t->skb->data; + skb_put(t->skb, iv_len); + memmove(pos + iv_len, pos, hdrlen); + skb_pull(t->skb, iv_len); + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED); + t->tx_info->control.hw_key = NULL; + t->hdr = (struct ieee80211_hdr *)t->skb->data; + } +} +#endif + +#if 0 +/* IV/ICV injection. */ +/* TODO: Quite unoptimal. It's better co modify mac80211 + * to reserve space for IV */ +static int +bes2600_tx_h_crypt(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + size_t iv_len; + size_t icv_len; + u8 *icv; + u8 *newhdr; + + if (!t->tx_info->control.hw_key || + !(t->hdr->frame_control & + __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) + return 0; + + iv_len = t->tx_info->control.hw_key->iv_len; + icv_len = t->tx_info->control.hw_key->icv_len; + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + icv_len += 8; /* MIC */ + + if ((skb_headroom(t->skb) + skb_tailroom(t->skb) < + iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || + (skb_headroom(t->skb) < + iv_len + WSM_TX_EXTRA_HEADROOM)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for crypto headers.\n" + "headroom: %d, tailroom: %d, " + "req_headroom: %d, req_tailroom: %d\n" + "Please fix it in bes2600_get_skb().\n", + skb_headroom(t->skb), skb_tailroom(t->skb), + iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); + return -ENOMEM; + } else if (skb_tailroom(t->skb) < icv_len) { + size_t offset = icv_len - skb_tailroom(t->skb); + u8 *p; + wiphy_warn(priv->hw->wiphy, + "Slowpath: tailroom is not big enough. " + "Req: %d, got: %d.\n", + icv_len, skb_tailroom(t->skb)); + + p = skb_push(t->skb, offset); + memmove(p, &p[offset], t->skb->len - offset); + skb_trim(t->skb, t->skb->len - offset); + } + + newhdr = skb_push(t->skb, iv_len); + memmove(newhdr, newhdr + iv_len, t->hdrlen); + t->hdr = (struct ieee80211_hdr *) newhdr; + t->hdrlen += iv_len; + icv = skb_put(t->skb, icv_len); + + return 0; +} +#else +static int +bes2600_tx_h_crypt(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + if (!t->tx_info->control.hw_key || + !ieee80211_has_protected(t->hdr->frame_control) || + (ieee80211_is_mgmt(t->hdr->frame_control) && + t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_SMS4)) + return 0; + + t->hdrlen += t->tx_info->control.hw_key->iv_len; + skb_put(t->skb, t->tx_info->control.hw_key->icv_len); + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_put(t->skb, 8); /* MIC space */ + + return 0; +} +#endif + +static int +bes2600_tx_h_align(struct bes2600_vif *priv, + struct bes2600_txinfo *t, + u8 *flags) +{ + size_t offset = (size_t)t->skb->data & 3; + + if (!offset) + return 0; + + if (offset & 1) { + wiphy_err(priv->hw->wiphy, + "Bug: attempt to transmit a frame " + "with wrong alignment: %ld\n", + (long)offset); + return -EINVAL; + } + + if (skb_headroom(t->skb) < offset) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for DMA alignment.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + bes2600_debug_tx_align(priv); + return 0; +} + +static int +bes2600_tx_h_action(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)t->hdr; + + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} + +/* Add WSM header */ +static struct wsm_tx * +bes2600_tx_h_wsm(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + struct wsm_tx *wsm; + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for WSM header.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return NULL; + } + + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queueId = + (t->txpriv.raw_link_id << 2) | wsm_queue_id_to_wsm(t->queue); + return wsm; +} + +/* BT Coex specific handling */ +static void +bes2600_tx_h_bt(struct bes2600_vif *priv, + struct bes2600_txinfo *t, + struct wsm_tx *wsm) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + u8 priority = 0; + + if (!hw_priv->is_BT_Present) + return; + + if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control))) + priority = WSM_EPTA_PRIORITY_MGT; + else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + u16 *ethertype = (u16 *) &payload[6]; + if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE))) + priority = WSM_EPTA_PRIORITY_EAPOL; + } else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control))) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (mgt_frame->u.assoc_req.listen_interval < + priv->listen_interval) { + bes2600_dbg(BES2600_DBG_TX_POLICY, + "Modified Listen Interval to %d from %d\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + * the one read from SDD */ + mgt_frame->u.assoc_req.listen_interval = + priv->listen_interval; + } + } + + if (likely(!priority)) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if (wsm->queueId == WSM_QUEUE_VOICE) + priority = WSM_EPTA_PRIORITY_VOICE; + else if (wsm->queueId == WSM_QUEUE_VIDEO) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } + + bes2600_dbg(BES2600_DBG_TXRX, "[TX] EPTA priority %d.\n", + priority); + + wsm->flags |= priority << 1; +} + +static int +bes2600_tx_h_rate_policy(struct bes2600_common *hw_priv, + struct bes2600_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + struct bes2600_vif *priv = + cw12xx_get_vif_from_ieee80211(t->tx_info->control.vif); + + t->txpriv.rate_id = tx_policy_get(hw_priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + if (t->txpriv.rate_id == BES2600_INVALID_RATE_ID) + return -EFAULT; + + wsm->flags |= t->txpriv.rate_id << 4; + + t->rate = bes2600_get_tx_rate(hw_priv, + &t->tx_info->control.rates[0]), + wsm->maxTxRate = t->rate->hw_value; + priv->hw_value = wsm->maxTxRate; + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (priv->association_mode.greenfieldMode) + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + + if (tx_policy_renew) { + bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Maybe it's better to reimplement task scheduling with + * a counter. */ + /* bes2600_tx_queues_lock(priv); */ + /* Definetly better. TODO. */ + wsm_lock_tx_async(hw_priv); + bes2600_tx_queues_lock(hw_priv); + if (queue_work(hw_priv->workqueue, + &hw_priv->tx_policy_upload_work) <= 0) { + bes2600_tx_queues_unlock(hw_priv); + wsm_unlock_tx(hw_priv); + } + } + return 0; +} + +static bool +bes2600_tx_h_pm_state(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + int was_buffered = 1; + + if (t->txpriv.link_id == priv->link_id_after_dtim && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) + queue_work(priv->hw_priv->workqueue, + &priv->multicast_start_work); + } + + if (t->txpriv.raw_link_id && t->txpriv.tid < BES2600_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1] + .buffered[t->txpriv.tid]++; + + return !was_buffered; +} + +static void +bes2600_tx_h_ba_stat(struct bes2600_vif *priv, + struct bes2600_txinfo *t) +{ + struct bes2600_common *hw_priv = priv->hw_priv; + + if (priv->join_status != BES2600_JOIN_STATUS_STA) + return; + if (!bes2600_is_ht(&hw_priv->ht_info)) + return; + if (!priv->setbssparams_done) + return; + if (!ieee80211_is_data(t->hdr->frame_control)) + return; + + spin_lock_bh(&hw_priv->ba_lock); + hw_priv->ba_acc += t->skb->len - t->hdrlen; + if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { + mod_timer(&hw_priv->ba_timer, + jiffies + BES2600_BLOCK_ACK_INTERVAL); + } + hw_priv->ba_cnt++; + spin_unlock_bh(&hw_priv->ba_lock); +} + +static int +bes2600_tx_h_skb_pad(struct bes2600_common *priv, + struct wsm_tx *wsm, + struct sk_buff *skb) +{ + size_t len = __le16_to_cpu(wsm->hdr.len); + size_t padded_len = priv->sbus_ops->align_size(priv->sbus_priv, len); + + if (WARN_ON(skb_padto(skb, padded_len) != 0)) { + return -EINVAL; + } + return 0; +} + +#ifdef CONFIG_BES2600_KEEP_ALIVE +static uint16_t find_idx_by_matched_paras(struct bes2600_common *hw_priv, uint8_t proto, uint16_t src_port, + uint16_t dst_port, uint32_t src_ip, uint32_t dst_ip) +{ + uint16_t idx; + struct ip_alive_cfg *iac = hw_priv->iac; + + for (idx = 0; idx < NUM_IP_FRAMES; idx++) { + if (iac[idx].bd.idx_used && + (proto == iac[idx].bd.proto) && + (src_port == iac[idx].bd.src_port) && + (dst_port == iac[idx].bd.dest_port)&& + (src_ip == iac[idx].bd.src_ip) && + (dst_ip == iac[idx].bd.dest_ip)) { + break; + } + } + + return idx; +} + +static int extract_ip_headers_info(struct bes2600_common *hw_priv, struct bes2600_vif *priv, uint8_t *iphdr) +{ + uint16_t _v_hl, _proto, src_port, dst_port, idx, tcp_bd_len; + uint8_t offset; + uint32_t src_ip, dst_ip; + struct ip_alive_cfg *iac = hw_priv->iac; + + bes2600_dbg_dump(BES2600_DBG_TEST_MODE, "iphdr:", iphdr, 64); + + /* Frame Control Flags, bit6: 1 for protected frame and 0 for non-protected frame; */ + ((iphdr[1] >> 6) & 0x1) ? (offset = 40) : (offset = 32); + + _v_hl = iphdr[offset + 2]; + _proto = iphdr[offset + 11]; + + /* + * _v_hl == 0x45 for ip packet; + * _proto == 6 for tcp protocol and 17 for udp protocol; + */ + if (_v_hl == 0x45) { + if (_proto == TCP_PROTO) { + /* obtain destination's listening port. */ + src_port = (iphdr[offset+22]<<8) + iphdr[offset+23]; + dst_port = (iphdr[offset+24]<<8) + iphdr[offset+25]; + src_ip = (iphdr[offset+14]<<24) + (iphdr[offset+15]<<16) + (iphdr[offset+16]<<8) + iphdr[offset+17]; + dst_ip = (iphdr[offset+18]<<24) + (iphdr[offset+19]<<16) + (iphdr[offset+20]<<8) + iphdr[offset+21]; + idx = find_idx_by_matched_paras(hw_priv, 1, src_port, dst_port, src_ip, dst_ip); + /* if port matches */ + if (idx < NUM_IP_FRAMES) { + iac[idx].iphd._len = (iphdr[offset+4]<<8) + iphdr[offset+5]; + iac[idx].iphd._proto = _proto; + iac[idx].tcphd._hdrlen_rsvd_flags = (iphdr[offset+34]<<8) + iphdr[offset+35]; + iac[idx].tcphd.seqno = (iphdr[offset+26]<<24) + (iphdr[offset+27]<<16) + (iphdr[offset+28]<<8) + iphdr[offset+29]; + iac[idx].tcphd.ackno = (iphdr[offset+30]<<24) + (iphdr[offset+31]<<16) + (iphdr[offset+32]<<8) + iphdr[offset+33]; + tcp_bd_len = iac[idx].iphd._len - sizeof(struct ip_header) - sizeof(struct tcp_header); + iac[idx].bd.next_seqno = iac[idx].tcphd.seqno + tcp_bd_len; + memcpy(iac[idx].bd.dest_mac, ((struct ieee80211_hdr *)iphdr)->addr3, 6); + + bes2600_dbg(BES2600_DBG_TEST_MODE, "src-ip:%x dest-ip:%x src-port:%d dest-port:%d\n", + iac[idx].iphd.src, + iac[idx].iphd.dest, + iac[idx].tcphd.src, + iac[idx].tcphd.dest); + bes2600_dbg(BES2600_DBG_TEST_MODE, "seqno:%u ackno:%u\n", iac[idx].tcphd.seqno, iac[idx].tcphd.ackno); + } + } + else if (_proto == UDP_PROTO) { + /* obtain destination's listening port. */ + src_port = (iphdr[offset+22]<<8) + iphdr[offset+23]; + dst_port = (iphdr[offset+24]<<8) + iphdr[offset+25]; + src_ip = (iphdr[offset+14]<<24) + (iphdr[offset+15]<<16) + (iphdr[offset+16]<<8) + iphdr[offset+17]; + dst_ip = (iphdr[offset+18]<<24) + (iphdr[offset+19]<<16) + (iphdr[offset+20]<<8) + iphdr[offset+21]; + idx = find_idx_by_matched_paras(hw_priv, 0, src_port, dst_port, src_ip, dst_ip); + /* if port matches */ + if (idx < NUM_IP_FRAMES) { + iac[idx].iphd._len = (iphdr[offset+4]<<8) + iphdr[offset+5]; + iac[idx].iphd._proto = _proto; + memcpy(iac[idx].bd.dest_mac, ((struct ieee80211_hdr *)iphdr)->addr3, 6); + } + } + } + + return 0; +} +#endif /* CONFIG_BES2600_KEEP_ALIVE */ + +/* ******************************************************************** */ + +void bes2600_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct bes2600_common *hw_priv = dev->priv; + struct bes2600_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = BES2600_MAX_TID, + .txpriv.rate_id = BES2600_INVALID_RATE_ID, +#ifdef P2P_MULTIVIF + .txpriv.raw_if_id = 0, +#endif + }; + struct ieee80211_sta *sta; + struct wsm_tx *wsm; + bool tid_update = 0; + u8 flags = 0; + int ret; + struct bes2600_vif *priv; + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + + if (!skb->data) + BUG_ON(1); + + if (!(t.tx_info->control.vif)) { + goto drop; + } + priv = cw12xx_get_vif_from_ieee80211(t.tx_info->control.vif); + if (!priv) + goto drop; + + if (atomic_read(&priv->enabled) == 0) + goto drop; + + /* wake up device if device is in low power mode */ + bes2600_pwr_set_busy_event_with_timeout_async( + hw_priv, BES_PWR_LOCK_ON_TX, BES_PWR_EVENT_TX_TIMEOUT); + +#ifdef CONFIG_BES2600_KEEP_ALIVE + /* extract ip & tcp header's information; */ + extract_ip_headers_info(hw_priv, priv, skb->data); +#endif + +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->start_stop_tsm.start) { + if (hw_priv->tsm_info.ac == t.queue) + hw_priv->tsm_stats.txed_msdu_count++; + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + + if ((ieee80211_is_action(frame->frame_control)) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + u8 *action = (u8*)&mgmt->u.action.category; + bes2600_check_go_neg_conf_success(hw_priv, action); + bes2600_check_prov_desc_req(hw_priv, action); + } + + t.txpriv.if_id = priv->if_id; + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + if (control) { + t.sta = control->sta; + t.sta_priv = (struct bes2600_sta_priv *)&t.sta->drv_priv; + } + + if (WARN_ON(t.queue >= 4)) + goto drop; + + /* + should not drop packets here, it may cause tx rate decreasing + tx flow control will be handled in bes2600_queue_put + */ +#if 0 + spin_lock_bh(&hw_priv->tx_queue[t.queue].lock); + + if ((priv->if_id == 0) && + (hw_priv->tx_queue[t.queue].num_queued_vif[0] >= + hw_priv->vif0_throttle)) { + spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); + goto drop; + } else if ((priv->if_id == 1) && + (hw_priv->tx_queue[t.queue].num_queued_vif[1] >= + hw_priv->vif1_throttle)) { + spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); + goto drop; + } + + spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); +#endif + + ret = bes2600_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; + + bes2600_dbg(BES2600_DBG_TXRX, "[TX] TX %d bytes (if_id: %d," + " queue: %d, link_id: %d (%d)).\n", + skb->len, priv->if_id, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + bes2600_tx_h_pm(priv, &t); + bes2600_tx_h_calc_tid(priv, &t); +#ifdef CONFIG_BES2600_WAPI_SUPPORT + bes2600_tx_wapi_shrink_iv_space(priv, &t); +#endif + ret = bes2600_tx_h_crypt(priv, &t); + if (ret) + goto drop; + ret = bes2600_tx_h_align(priv, &t, &flags); + if (ret) + goto drop; + ret = bes2600_tx_h_action(priv, &t); + if (ret) + goto drop; + wsm = bes2600_tx_h_wsm(priv, &t); + if (!wsm) { + ret = -ENOMEM; + goto drop; + } +#ifdef CONFIG_BES2600_TESTMODE + flags |= WSM_TX_FLAG_EXPIRY_TIME; +#endif /*CONFIG_BES2600_TESTMODE*/ + wsm->flags |= flags; + bes2600_tx_h_bt(priv, &t, wsm); + ret = bes2600_tx_h_rate_policy(hw_priv, &t, wsm); + if (ret) + goto drop; + + ret = bes2600_tx_h_skb_pad(hw_priv, wsm, skb); + if (ret) + goto drop; + rcu_read_lock(); + sta = rcu_dereference(t.sta); + + bes2600_tx_h_ba_stat(priv, &t); + + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = bes2600_tx_h_pm_state(priv, &t); + BUG_ON(bes2600_queue_put(&hw_priv->tx_queue[t.queue], + t.skb, &t.txpriv)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)) + if (skb->sk) + sk_pacing_shift_update(skb->sk, 7); +#endif + + bes2600_dbg(BES2600_DBG_ROC, "QPUT %x, %pM, if_id - %d\n", + t.hdr->frame_control, t.da, priv->if_id); + } + spin_unlock_bh(&priv->ps_state_lock); + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + if (tid_update && sta) + ieee80211_sta_set_buffered(sta, + t.txpriv.tid, true); +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ + + rcu_read_unlock(); + + bes2600_bh_wakeup(hw_priv); + + return; + +drop: + bes2600_skb_dtor(hw_priv, skb, &t.txpriv); + return; +} + +/* ******************************************************************** */ + +static int bes2600_handle_pspoll(struct bes2600_vif *priv, + struct sk_buff *skb) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = + (struct ieee80211_pspoll *) skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int drop = 1; + int i; + + if (priv->join_status != BES2600_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (sta) { + struct bes2600_sta_priv *sta_priv; + sta_priv = (struct bes2600_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); + } + rcu_read_unlock(); + if (!link_id) + goto done; + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is + * queued already. */ + for (i = 0; i < 4; ++i) { + if (bes2600_queue_get_num_queued(priv, + &hw_priv->tx_queue[i], + pspoll_mask)) { + bes2600_bh_wakeup(hw_priv); + drop = 1; + break; + } + } + bes2600_info(BES2600_DBG_TXRX, "[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); +done: + return drop; +} + +/* ******************************************************************** */ + +void bes2600_tx_confirm_cb(struct bes2600_common *hw_priv, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = bes2600_queue_get_queue_id(arg->packetID); + struct bes2600_queue *queue = &hw_priv->tx_queue[queue_id]; + struct sk_buff *skb; + const struct bes2600_txpriv *txpriv; + struct bes2600_vif *priv; +#ifdef CONFIG_BES2600_TESTMODE + u16 pkt_delay; +#endif + + bes2600_dbg(BES2600_DBG_TXRX, "[TX] TX confirm: %d, %d.\n", + arg->status, arg->ackFailures); + + if (unlikely(bes2600_itp_tx_running(hw_priv))) + return; + + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, arg->if_id); + if (unlikely(!priv)) + return; + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + spin_unlock(&priv->vif_lock); + return; + } + + if (WARN_ON(queue_id >= 4)) { + spin_unlock(&priv->vif_lock); + return; + } + + if (arg->status) + bes2600_dbg(BES2600_DBG_TXRX, "TX failed: %d.\n", + arg->status); + +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if ((arg->status == WSM_STATUS_RETRY_EXCEEDED) || + (arg->status == WSM_STATUS_TX_LIFETIME_EXCEEDED)) { + hw_priv->tsm_stats.msdu_discarded_count++; + } else if ((hw_priv->start_stop_tsm.start) && + (arg->status == WSM_STATUS_SUCCESS)) { + if (queue_id == hw_priv->tsm_info.ac) { + struct timeval tmval; + do_gettimeofday(&tmval); + pkt_delay = hw_priv->start_stop_tsm.packetization_delay; + if (hw_priv->tsm_info.sta_roamed && + !hw_priv->tsm_info.use_rx_roaming) { + hw_priv->tsm_info.roam_delay = tmval.tv_usec - + hw_priv->tsm_info.txconf_timestamp_vo; + if (hw_priv->tsm_info.roam_delay > pkt_delay) + hw_priv->tsm_info.roam_delay -= pkt_delay; + bes2600_info(BES2600_DBG_TEST_MODE, "[TX] txConf" + "Roaming: roam_delay = %u\n", + hw_priv->tsm_info.roam_delay); + hw_priv->tsm_info.sta_roamed = 0; + } + hw_priv->tsm_info.txconf_timestamp_vo = tmval.tv_usec; + } + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = arg->link_id, + .stop = 1, + .multicast = !arg->link_id, + .if_id = arg->if_id, + }; + bes2600_suspend_resume(priv, &suspend); + wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d)." + " STAs asleep: 0x%.8X\n", + arg->link_id, + bes2600_queue_get_generation(arg->packetID) + 1, + priv->sta_asleep_mask); +#ifdef CONFIG_BES2600_TESTMODE + WARN_ON(bes2600_queue_requeue(hw_priv, queue, + arg->packetID, true)); +#else + WARN_ON(bes2600_queue_requeue(queue, + arg->packetID, true)); +#endif + spin_lock_bh(&priv->ps_state_lock); + if (!arg->link_id) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) { + queue_work(hw_priv->workqueue, + &priv->multicast_start_work); + } + } + spin_unlock_bh(&priv->ps_state_lock); + spin_unlock(&priv->vif_lock); + } else if (!(bes2600_queue_get_skb( + queue, arg->packetID, &skb, &txpriv))) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + int tx_count = arg->ackFailures; + u8 ht_flags = 0; + int i; + +#ifndef P2P_MULTIVIF + if (txpriv->offchannel_if_id) + bes2600_dbg(BES2600_DBG_ROC, "TX CONFIRM %x - %d - %d\n", + skb->data[txpriv->offset], + txpriv->offchannel_if_id, arg->status); +#endif + if (priv->association_mode.greenfieldMode) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + if (likely(!arg->status)) { + tx->flags |= IEEE80211_TX_STAT_ACK; + priv->cqm_tx_failure_count = 0; + ++tx_count; + bes2600_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ + bes2600_debug_txed_agg(priv); + } + } else { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMING && + priv->bss_loss_confirm_id == arg->packetID) { + priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMED; + spin_unlock(&priv->bss_loss_lock); + cancel_delayed_work(&priv->bss_loss_work); + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, BSS_LOSS_CFM_INV * HZ / 1000); + } else { + spin_unlock(&priv->bss_loss_lock); + } + + /* TODO: Update TX failure counters */ + if (unlikely(priv->cqm_tx_failure_thold && + (++priv->cqm_tx_failure_count > + priv->cqm_tx_failure_thold))) { + priv->cqm_tx_failure_thold = 0; + queue_work(hw_priv->workqueue, + &priv->tx_failure_work); + } + if (tx_count) + ++tx_count; + } + spin_unlock(&priv->vif_lock); + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } +#ifdef KEY_FRAME_SW_RETRY + if (bes2600_bh_sw_process(hw_priv, arg) == 0) { + return; + } +#endif + +#ifdef CONFIG_BES2600_TESTMODE + bes2600_queue_remove(hw_priv, queue, arg->packetID); +#else + bes2600_queue_remove(queue, arg->packetID); +#endif /*CONFIG_BES2600_TESTMODE*/ + }else { + spin_unlock(&priv->vif_lock); + return; + } +} + +static void bes2600_notify_buffered_tx(struct bes2600_vif *priv, + struct sk_buff *skb, int link_id, int tid) +{ +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + + if (link_id && tid < BES2600_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->ps_state_lock); + if (!WARN_ON(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->ps_state_lock); + + if (!still_buffered && tid < BES2600_MAX_TID) { + hdr = (struct ieee80211_hdr *) skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */ +} + +void bes2600_skb_dtor(struct bes2600_common *hw_priv, + struct sk_buff *skb, + const struct bes2600_txpriv *txpriv) +{ + struct bes2600_vif *priv = + __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + + skb_pull(skb, txpriv->offset); + if (priv && txpriv->rate_id != BES2600_INVALID_RATE_ID) { + bes2600_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(hw_priv, txpriv->rate_id); + } + if (likely(!bes2600_is_itp(hw_priv))) { + ieee80211_tx_status(hw_priv->hw, skb); + bes2600_tx_status(priv, skb); + } + +} +#ifdef CONFIG_BES2600_TESTMODE +/* TODO It should be removed before official delivery */ +static void frame_hexdump(char *prefix, u8 *data, int len) +{ + bes2600_dbg_dump(BES2600_DBG_TXRX, prefix, data, len); +} +/** + * bes2600_tunnel_send_testmode_data - Send test frame to the driver + * + * @priv: pointer to bes2600 private structure + * @skb: skb with frame + * + * Returns: 0 on success or non zero value on failure + */ +static int bes2600_tunnel_send_testmode_data(struct bes2600_common *hw_priv, + struct sk_buff *skb) +{ + if (bes2600_testmode_event(hw_priv->hw->wiphy, BES_MSG_EVENT_FRAME_DATA, + skb->data, skb->len, GFP_ATOMIC)) + return -EINVAL; + + return 0; +} + +/** + * bes2600_frame_test_detection - Detection frame_test + * + * @priv: pointer to bes2600 vif structure + * @frame: ieee80211 header + * @skb: skb with frame + * + * Returns: 1 - frame test detected, 0 - not detected + */ +static int bes2600_frame_test_detection(struct bes2600_vif *priv, + struct ieee80211_hdr *frame, + struct sk_buff *skb) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + int hdrlen = ieee80211_hdrlen(frame->frame_control); + int detected = 0; + int ret; + + if (hdrlen + hw_priv->test_frame.len <= skb->len && + memcmp(skb->data + hdrlen, hw_priv->test_frame.data, + hw_priv->test_frame.len) == 0) { + detected = 1; + bes2600_dbg(BES2600_DBG_TEST_MODE, "TEST FRAME detected"); + frame_hexdump("TEST FRAME original:", skb->data, skb->len); + ret = ieee80211_data_to_8023(skb, hw_priv->mac_addr, + priv->mode); + if (!ret) { + frame_hexdump("FRAME 802.3:", skb->data, skb->len); + ret = bes2600_tunnel_send_testmode_data(hw_priv, skb); + } + if (ret) + bes2600_err(BES2600_DBG_TEST_MODE, "Send TESTFRAME failed(%d)", ret); + } + return detected; +} +#endif /* CONFIG_BES2600_TESTMODE */ + + +static void +bes2600_rx_h_ba_stat(struct bes2600_vif *priv, + size_t hdrlen, size_t skb_len ) +{ + struct bes2600_common *hw_priv = priv->hw_priv; + if (priv->join_status != BES2600_JOIN_STATUS_STA) + return; + if (!bes2600_is_ht(&hw_priv->ht_info)) + return; + if (!priv->setbssparams_done) + return; + + spin_lock_bh(&hw_priv->ba_lock); + hw_priv->ba_acc_rx += skb_len - hdrlen; + if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { + mod_timer(&hw_priv->ba_timer, + jiffies + BES2600_BLOCK_ACK_INTERVAL); + } + hw_priv->ba_cnt_rx++; + spin_unlock_bh(&hw_priv->ba_lock); +} + +void bes2600_rx_cb(struct bes2600_vif *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct bes2600_link_entry *entry = NULL; + bool early_data = false; + size_t hdrlen = 0; + + hdr->flag = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + hdr->boottime_ns = ktime_get_boot_ns(); +#endif + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + goto drop; + } + + /* wakeup device based on frame type */ + if (!is_multicast_ether_addr(ieee80211_get_DA(frame))) { + /* for unicast, wakeup device directly */ + bes2600_pwr_set_busy_event_with_timeout_async( + hw_priv, BES_PWR_LOCK_ON_RX, BES_PWR_EVENT_RX_TIMEOUT); + } + + if ((ieee80211_is_action(frame->frame_control)) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + u8 *action = (u8*)&mgmt->u.action.category; + bes2600_check_go_neg_conf_success(hw_priv, action); + } + +#ifdef CONFIG_BES2600_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->start_stop_tsm.start) { + unsigned queue_id = skb_get_queue_mapping(skb); + if (queue_id == 0) { + struct timeval tmval; + do_gettimeofday(&tmval); + if (hw_priv->tsm_info.sta_roamed && + hw_priv->tsm_info.use_rx_roaming) { + hw_priv->tsm_info.roam_delay = tmval.tv_usec - + hw_priv->tsm_info.rx_timestamp_vo; + bes2600_dbg(BES2600_DBG_TEST_MODE, "[RX] RxInd Roaming:" + "roam_delay = %u\n", hw_priv->tsm_info.roam_delay); + hw_priv->tsm_info.sta_roamed = 0; + } + hw_priv->tsm_info.rx_timestamp_vo = tmval.tv_usec; + } + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_BES2600_TESTMODE*/ + if (arg->link_id && (arg->link_id != BES2600_LINK_ID_UNMAPPED) + && (arg->link_id <= BES2600_MAX_STA_IN_AP_MODE)) { + entry = &priv->link_id_db[arg->link_id - 1]; + if (entry->status == BES2600_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + else if ((arg->link_id == BES2600_LINK_ID_UNMAPPED) + && (priv->vif->p2p == WSM_START_MODE_P2P_GO) + && ieee80211_is_action(frame->frame_control) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + bes2600_dbg(BES2600_DBG_TXRX, "[RX] Going to MAP&RESET link ID\n"); + + if (work_pending(&priv->linkid_reset_work)) + WARN_ON(1); + + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = 0; + schedule_work(&priv->linkid_reset_work); + } + + if (arg->link_id && (arg->link_id != BES2600_LINK_ID_UNMAPPED) + && (priv->vif->p2p == WSM_START_MODE_P2P_GO) + && ieee80211_is_action(frame->frame_control) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + /* Link ID already exists for the ACTION frame. + * Reset and Remap */ + if (work_pending(&priv->linkid_reset_work)) + WARN_ON(1); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = arg->link_id; + schedule_work(&priv->linkid_reset_work); + } +#endif + if (unlikely(arg->status)) { + if (arg->status == WSM_STATUS_MICFAILURE) { + bes2600_warn(BES2600_DBG_TXRX, "[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + bes2600_dbg(BES2600_DBG_TXRX, "[RX] No key found.\n"); + goto drop; + } else { + bes2600_warn(BES2600_DBG_TXRX, "[RX] Receive failure: %d.\n", + arg->status); + goto drop; + } + } + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " + "Size is lesser than IEEE header.\n"); + goto drop; + } + + if (unlikely(ieee80211_is_pspoll(frame->frame_control))) + if (bes2600_handle_pspoll(priv, skb)) + goto drop; + + hdr->mactime = 0; /* Not supported by WSM */ + hdr->band = (arg->channelNumber > 14) ? + NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channelNumber, + hdr->band); + + if (arg->rxedRate >= 14) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + hdr->encoding |= RX_ENC_HT; +#else + hdr->flag |= RX_FLAG_HT; +#endif + hdr->rate_idx = arg->rxedRate - 14; + } else if (arg->rxedRate >= 4) { + if (hdr->band == NL80211_BAND_5GHZ) + hdr->rate_idx = arg->rxedRate - 6; + else + hdr->rate_idx = arg->rxedRate - 2; + } else { + hdr->rate_idx = arg->rxedRate; + } + + hdr->signal = (s8)arg->rcpiRssi; + hdr->antenna = 0; + + hdrlen = ieee80211_hdrlen(frame->frame_control); + if (WSM_RX_STATUS_ENCRYPTION(arg->flags) == WSM_RX_STATUS_DECRYPTED) { + /* decrypted frame with iv/icv stripped */ + hdr->flag |= RX_FLAG_DECRYPTED; + hdr->flag |= RX_FLAG_IV_STRIPPED; + } + else if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + size_t iv_len = 0, icv_len = 0; + + hdr->flag |= RX_FLAG_DECRYPTED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed.*/ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + hdr->flag |= RX_FLAG_IV_STRIPPED; + break; + default: + WARN_ON("Unknown encryption type"); + goto drop; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == WSM_STATUS_MICFAILURE) { + icv_len = 0; + hdr->flag |= RX_FLAG_IV_STRIPPED; + } + + if (skb->len < hdrlen + iv_len + icv_len) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " + "Size is lesser than crypto headers.\n"); + goto drop; + } + + /* Protocols not defined in mac80211 should be + stripped/crypted in driver/firmware */ + if (WSM_RX_STATUS_ENCRYPTION(arg->flags) == + WSM_RX_STATUS_WAPI) { + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + + } + + bes2600_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + bes2600_debug_rxed_agg(priv); + + if (ieee80211_is_beacon(frame->frame_control) && + !arg->status && + !memcmp(ieee80211_get_SA(frame), priv->join_bssid, + ETH_ALEN)) { + const u8 *tim_ie; + u8 *ies; + size_t ies_len; + priv->disable_beacon_filter = false; + queue_work(hw_priv->workqueue, &priv->update_filtering_work); + ies = ((struct ieee80211_mgmt *) + (skb->data))->u.beacon.variable; + ies_len = skb->len - (ies - (u8 *)(skb->data)); + + tim_ie = bes2600_get_ie(ies, ies_len, WLAN_EID_TIM); + if (tim_ie) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *)&tim_ie[2]; + + if (priv->join_dtim_period != tim->dtim_period) { + priv->join_dtim_period = tim->dtim_period; + queue_work(hw_priv->workqueue, + &priv->set_beacon_wakeup_period_work); + } + } + if (unlikely(priv->disable_beacon_filter)) { + priv->disable_beacon_filter = false; + queue_work(hw_priv->workqueue, + &priv->update_filtering_work); + } + } +#ifdef AP_HT_CAP_UPDATE + if (priv->mode == NL80211_IFTYPE_AP && + ieee80211_is_beacon(frame->frame_control) && + !arg->status){ + + u8 *ies; + size_t ies_len; + const u8 *ht_cap; + ies = ((struct ieee80211_mgmt *) + (skb->data))->u.beacon.variable; + ies_len = skb->len - (ies - (u8 *)(skb->data)); + ht_cap = bes2600_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY); + if(!ht_cap){ + priv->ht_info |= 0x0011; + } + queue_work(hw_priv->workqueue, + &priv->ht_info_update_work); + + } +#endif + +#ifdef ROAM_OFFLOAD + if ((ieee80211_is_beacon(frame->frame_control)||ieee80211_is_probe_resp(frame->frame_control)) && + !arg->status ) { + if (hw_priv->auto_scanning && !atomic_read(&hw_priv->scan.in_progress)) + hw_priv->frame_rcvd = 1; + + if (!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) { + if (hw_priv->beacon) + dev_kfree_skb(hw_priv->beacon); + hw_priv->beacon = skb_copy(skb, GFP_ATOMIC); + if (!hw_priv->beacon) + bes2600_err(BES2600_DBG_TXRX, "bes2600: sched_scan: own beacon storing failed\n"); + } + } +#endif /*ROAM_OFFLOAD*/ + + if (ieee80211_is_deauth(frame->frame_control) || + ieee80211_is_disassoc(frame->frame_control)) + bes2600_pwr_set_busy_event_with_timeout_async(hw_priv, + BES_PWR_LOCK_ON_DISCON, 1500); + + + if (ieee80211_is_data(frame->frame_control)) { + bes2600_rx_h_ba_stat(priv, hdrlen, skb->len); + bes2600_rx_status(priv, skb); + } + +#ifdef CONFIG_BES2600_TESTMODE + if (hw_priv->test_frame.len > 0 && + priv->mode == NL80211_IFTYPE_STATION) { + if (bes2600_frame_test_detection(priv, frame, skb) == 1) { + consume_skb(skb); + *skb_p = NULL; + return; + } + } +#endif /* CONFIG_BES2600_TESTMODE */ + + if (unlikely(bes2600_itp_rxed(hw_priv, skb))) + consume_skb(skb); + else if (unlikely(early_data)) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == BES2600_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } + *skb_p = NULL; + + return; + +drop: + /* TODO: update failure counters */ + return; +} + +/* ******************************************************************** */ +/* Security */ + +int bes2600_alloc_key(struct bes2600_common *hw_priv) +{ + int idx; + + idx = ffs(~hw_priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + hw_priv->key_map |= BIT(idx); + hw_priv->keys[idx].entryIndex = idx; + return idx; +} + +void bes2600_free_key(struct bes2600_common *hw_priv, int idx) +{ + BUG_ON(!(hw_priv->key_map & BIT(idx))); + memset(&hw_priv->keys[idx], 0, sizeof(hw_priv->keys[idx])); + hw_priv->key_map &= ~BIT(idx); +} + +void bes2600_free_keys(struct bes2600_common *hw_priv) +{ + memset(&hw_priv->keys, 0, sizeof(hw_priv->keys)); + hw_priv->key_map = 0; +} + +int bes2600_upload_keys(struct bes2600_vif *priv) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + int idx, ret = 0; + + for (idx = 0; idx <= WSM_KEY_MAX_IDX; ++idx) + if (hw_priv->key_map & BIT(idx)) { + ret = wsm_add_key(hw_priv, &hw_priv->keys[idx], priv->if_id); + if (ret < 0) + break; + } + return ret; +} +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) +/* Workaround for WFD test case 6.1.10 */ +void bes2600_link_id_reset(struct work_struct *work) +{ + struct bes2600_vif *priv = + container_of(work, struct bes2600_vif, linkid_reset_work); + struct bes2600_common *hw_priv = priv->hw_priv; + int temp_linkid; + + if (!priv->action_linkid) { + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = bes2600_alloc_link_id(priv, + &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(hw_priv->workqueue); + /* Release the link ID */ + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].prev_status = + priv->link_id_db[temp_linkid - 1].status; + priv->link_id_db[temp_linkid - 1].status = + BES2600_LINK_RESET; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, + &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } else { + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[priv->action_linkid - 1].prev_status = + priv->link_id_db[priv->action_linkid - 1].status; + priv->link_id_db[priv->action_linkid - 1].status = + BES2600_LINK_RESET_REMAP; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + flush_workqueue(hw_priv->workqueue); + } +} +#endif diff --git a/drivers/staging/bes2600/txrx.h b/drivers/staging/bes2600/txrx.h new file mode 100644 index 000000000000..0825f44f4d35 --- /dev/null +++ b/drivers/staging/bes2600/txrx.h @@ -0,0 +1,99 @@ +/* + * Datapath interface for BES2600 mac80211 drivers + * + * Copyright (c) 2010, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_TXRX_H +#define BES2600_TXRX_H + +#include + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; +/* extern */ struct wsm_tx_confirm; +/* extern */ struct bes2600_txpriv; +/* extern */ struct bes2600_vif; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; /* TODO: u32 or u8, profile and select best */ + u8 usage_count; /* --// -- */ + u8 retry_count; /* --// -- */ + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct bes2600_common *hw_priv); +void tx_policy_deinit(struct bes2600_common *hw_priv); +void tx_policy_upload_work(struct work_struct *work); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 bes2600_rate_mask_to_wsm(struct bes2600_common *hw_priv, + u32 rates); +void bes2600_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +void bes2600_skb_dtor(struct bes2600_common *hw_priv, + struct sk_buff *skb, + const struct bes2600_txpriv *txpriv); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void bes2600_tx_confirm_cb(struct bes2600_common *hw_priv, + struct wsm_tx_confirm *arg); +void bes2600_rx_cb(struct bes2600_vif *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* Timeout */ + +void bes2600_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int bes2600_alloc_key(struct bes2600_common *hw_priv); +void bes2600_free_key(struct bes2600_common *hw_priv, int idx); +void bes2600_free_keys(struct bes2600_common *hw_priv); +int bes2600_upload_keys(struct bes2600_vif *priv); + +/* ******************************************************************** */ +/* Workaround for WFD test case 6.1.10 */ +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) +void bes2600_link_id_reset(struct work_struct *work); +#endif + +#endif /* BES2600_TXRX_H */ diff --git a/drivers/staging/bes2600/txrx_opt.c b/drivers/staging/bes2600/txrx_opt.c new file mode 100644 index 000000000000..15f6abbb666f --- /dev/null +++ b/drivers/staging/bes2600/txrx_opt.c @@ -0,0 +1,467 @@ +/*************************************************************************** + * + * Copyright 2015-2022 BES. + * All rights reserved. All unpublished rights reserved. + * + * No part of this work may be used or reproduced in any form or by any + * means, or stored in a database or retrieval system, without prior written + * permission of BES. + * + * Use of this work is governed by a license granted by BES. + * This work contains confidential and proprietary information of + * BES. which is protected by copyright, trade secret, + * trademark and other intellectual property rights. + * + ****************************************************************************/ +#include +#include +#include +#include + +#include "bes2600.h" +#include "wsm.h" +#include "bh.h" +#include "ap.h" +#include "debug.h" +#include "sta.h" +#include "sbus.h" +#include "bes2600_log.h" +#include "bes_pwr.h" + +#define TXRX_OPT_CLOSE_EDCA 0 +#define TXRX_OPT_EDCA_MAX_LEVEL 4 +#define TX_AVG_TIME_COUNT 10 +#define TXRX_OPT_PEROID 500 +#define TXRX_OPT_DEBUG 1 + +#define TXRX_HIGH_TP_THRESHOLD_2G4 30000 //unit is kbps +#define TXRX_HIGH_TP_THRESHOLD_5G 40000 //unit is kbps +#define TXRX_HIGH_TP_DELTA_TIME_2G4 8 //unit ms +#define TXRX_HIGH_TP_DELTA_TIME_5G 6 //unit ms + +static uint32_t tx_delta_time_arr[4][TX_AVG_TIME_COUNT]; +static uint32_t tx_queue_arr[4] = {0}; +static uint32_t tx_delta_time_total = 0; +static uint32_t tx_delta_time_total_cnt = 0; + +void bes2600_add_tx_delta_time(uint32_t tx_delta_time) +{ + tx_delta_time_total += tx_delta_time; + tx_delta_time_total_cnt++; +} + +uint32_t bes2600_get_tx_delta_time(void) +{ + if (tx_delta_time_total_cnt != 0) + return tx_delta_time_total / tx_delta_time_total_cnt; + else + return 0; +} + +void bes2600_clear_tx_delta_time(void) +{ + tx_delta_time_total_cnt = 0; + tx_delta_time_total = 0; + return ; +} + +uint32_t bes2600_get_tx_ac_delta_time(int ac) +{ + uint32_t avg_time = 0; + int i = 0; + for (i = 0; i < TX_AVG_TIME_COUNT; i++) { + avg_time += tx_delta_time_arr[ac][i]; + } + return avg_time / TX_AVG_TIME_COUNT; +} + +void bes2600_clear_tx_ac_delta_time(int ac) +{ + int i = 0; + for (i = 0; i < TX_AVG_TIME_COUNT; i++) { + tx_delta_time_arr[ac][i] = 0; + } + return ; +} + +void bes2600_add_tx_ac_delta_time(int ac, uint32_t del_time) +{ +#if 0 + if (tx_queue_arr[ac] >= (TX_AVG_TIME_COUNT - 1)) { + static int num = 0; + if ((num ++ % 10) == 0) + bes2600_info(BES2600_DBG_TXRX_LVL, "%s %d %d %d %d %d del=%d\n\r", __func__, tx_delta_time_arr[ac][0], + tx_delta_time_arr[ac][2], tx_delta_time_arr[ac][4], tx_delta_time_arr[ac][6], + tx_delta_time_arr[ac][8], del_time); + } +#endif + tx_delta_time_arr[ac][tx_queue_arr[ac]] = del_time; + tx_queue_arr[ac] = (tx_queue_arr[ac] >= (TX_AVG_TIME_COUNT - 1)) ? 0 : (tx_queue_arr[ac] + 1); + +} + + +int bes2600_set_txrx_opt_param(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + MIB_TXRX_OPT_PARAM *para) +{ + + int ret = 0; + ret = WARN_ON(wsm_write_mib(hw_priv, + WSM_MIB_ID_EXT_TXRX_OPT_PARAM, + (u8 *)para, + sizeof(MIB_TXRX_OPT_PARAM), + priv->if_id)); + return ret; +} + +int bes2600_enable_tx_shortgi(struct bes2600_common *hw_priv, + struct bes2600_vif *priv, + u8 onoff) +{ + int ret = 0; + static u8 en = 0xff; + + bes2600_dbg(BES2600_DBG_TXRX_OPT, "%s onoff=%d\n\r", __func__, onoff); + + if (en != onoff) { + en = onoff; + ret = WARN_ON(wsm_write_mib(hw_priv, + WSM_MIB_ID_EXT_TX_SHORT_GI_ENABLED, + (u8 *)&onoff, + sizeof(onoff), + priv->if_id)); + } + return ret; +} + +void bes2600_rx_status(struct bes2600_vif *priv, struct sk_buff *skb) +{ + priv->dot11ReceivedFragmentCount++; + priv->dot11ReceivedDataBytes += skb->len; +} + +void bes2600_tx_status(struct bes2600_vif *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + __le16 fc; + int i; + int retry_count = -1; + fc = hdr->frame_control; + + if (ieee80211_is_data(fc)) { + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + if (info->status.rates[i].idx < 0) { + break; + } + retry_count += info->status.rates[i].count; + } + if (retry_count < 0) + retry_count = 0; + + if (info->flags & IEEE80211_TX_STAT_ACK) { + priv->dot11TransmittedFrameCount++; + priv->dot11TransmittedDataBytes += (skb->len + 4); + if (retry_count > 0) + priv->dot11RetryCount += retry_count; + + } else { + /* tx fail.*/ + priv->dot11FailedCount++; + } + } + +} +void bes2600_set_default_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv); + +int bes2600_set_high_edca_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int level) +{ + struct wsm_edca_params arg; + int i = 0; + static int lev = 0; + bes2600_dbg(BES2600_DBG_TXRX_OPT, "set edca level=%d\n\r", level); + + if (lev == level) + return 0; + + memcpy(&arg, &(priv->edca), sizeof(struct wsm_edca_params)); + + + if (level == 0) { + bes2600_set_default_params(hw_priv, priv); + return 0; + } else if (level == 1) { + for ( i = 0; i < 4; i++) { + arg.params[i].aifns = 2; + arg.params[i].cwMax = 7; + arg.params[i].cwMin = 3; + /* + * tx op must set 0 + * set other, some AP may not response BA when rx data. + */ + arg.params[i].txOpLimit = 0; + arg.params[i].maxReceiveLifetime = 0xc8; + } + } else if (level == 2) { + for ( i = 0; i < 4; i++) { + arg.params[i].aifns = 2; + arg.params[i].cwMax = 5; + arg.params[i].cwMin = 1; + arg.params[i].txOpLimit = 0; + arg.params[i].maxReceiveLifetime = 0xc8; + } + } else if (level == 3) { + for ( i = 0; i < 4; i++) { + arg.params[i].aifns = 2; + arg.params[i].cwMax = 3; + arg.params[i].cwMin = 1; + arg.params[i].txOpLimit = 0; + arg.params[i].maxReceiveLifetime = 0xc8; + } + } else if (level == 4) { + for ( i = 0; i < 4; i++) { + arg.params[i].aifns = 1; + arg.params[i].cwMax = 3; + arg.params[i].cwMin = 1; + arg.params[i].txOpLimit = 0; + arg.params[i].maxReceiveLifetime = 0xc8; + } + } + wsm_set_edca_params(hw_priv, &arg, priv->if_id); + return 0; +} +void bes2600_set_default_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv) +{ + bes2600_dbg(BES2600_DBG_TXRX_OPT, "set edca default\n\r"); + wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); +} +void bes2600_set_cca_method(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int value) +{ +// todo set cca alg +} + +void bes2600_set_dynamic_agc(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int value) +{ +// todo set agc alg +} +int bes2600_get_tx_av_max_delta_time(void) +{ + int max_avg = 0; + int i = 0; + + for (i = 0; i < 4; i++) { + if (max_avg < bes2600_get_tx_ac_delta_time(i)) { + max_avg = bes2600_get_tx_ac_delta_time(i); + } + //bes2600_clear_tx_ac_delta_time(i); + } + return max_avg; +} + +bool bes2600_station_is_ap_ht40(struct bes2600_common *hw_priv) +{ + + if (hw_priv->hw) { + struct ieee80211_conf *conf = &hw_priv->hw->conf; + if (conf != NULL) + if (conf->chandef.width == NL80211_CHAN_WIDTH_40) + return true; + } + return false; +} + +void bes2600_dynamic_opt_rxtx(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int rssi) +{ + u32 succPro = 0, tx_cnt, tx_retry, rx_cnt, tx_fail; + static u32 l_tx_cnt = 0, l_tx_fail = 0, l_tx_retry = 0, l_rx_cnt = 0; + static u32 tx_bps = 0, rx_bps = 0; + u32 total_kbps = 0; + static int level; + int rts_value = 2347; + int short_gi = 0; + + /* calculate real time throughput */ + if (hw_priv == NULL || priv == NULL) { + return; + } + tx_bps = abs (priv->dot11TransmittedDataBytes - tx_bps); + rx_bps = abs (priv->dot11ReceivedDataBytes - rx_bps); + total_kbps = (tx_bps / 128 + rx_bps / 128); + + total_kbps *= 1000; + total_kbps /= TXRX_OPT_PEROID; + + /* if tx/rx < 100k/s, close*/ + if (total_kbps < 100) { + level = 0; + goto txrx_opt_clear; + } + + /* calculate tx_cnt, tx_retry, rx_cnt */ + tx_cnt = (priv->dot11TransmittedFrameCount - l_tx_cnt); + tx_fail = (priv->dot11FailedCount - l_tx_fail); + tx_retry = (priv->dot11RetryCount - l_tx_retry); + rx_cnt = (priv->dot11ReceivedFragmentCount - l_rx_cnt); + ( (tx_cnt + tx_retry) > 0 ) ? (succPro = tx_cnt * 100 / (tx_cnt + tx_retry)) : (succPro = 0); + + if (succPro != 0) { + /* hw value 21 is mcs7, dynamic set short GI */ + if (priv->hw_value == 21 && succPro > 90) + short_gi = 1; + else + short_gi = 0; + + /* dynamic set RTS/CTS */ + if (succPro < 80) + rts_value = 512; + } + + /* dynamic set edca param */ + if (succPro != 0) { + if (bes2600_station_is_ap_ht40(hw_priv)) { + if (bes2600_get_tx_delta_time() > 8 || bes2600_get_tx_av_max_delta_time() > 8) { + if (level < 4) + level++; + } else { + if (level > 0) + level--; + } + /* high throughput force level = 0 */ + if (total_kbps > TXRX_HIGH_TP_THRESHOLD_5G && level > 0 && priv->hw_value > 19) { + level = 0; + } + + } else {//shiled room 13, office 8 + if (bes2600_get_tx_delta_time() > (TXRX_HIGH_TP_DELTA_TIME_5G + total_kbps / 8000) + || bes2600_get_tx_av_max_delta_time() > (TXRX_HIGH_TP_DELTA_TIME_5G + total_kbps / 8000)) { + if (level < 4) + level++; + } else { + if (level > 0) + level--; + } + /* high throughput force level = 0 */ + if (total_kbps > TXRX_HIGH_TP_THRESHOLD_2G4 && level > 0) { + level = 0; + } + + } + } +#if TXRX_OPT_CLOSE_EDCA + level = 0; +#endif + if (level > TXRX_OPT_EDCA_MAX_LEVEL) + level = TXRX_OPT_EDCA_MAX_LEVEL; + + bes2600_dbg(BES2600_DBG_TXRX_OPT, "txrx_opt: tx(cnt=%d retry=%d psr=%d tx_fail=%d (wsm rts=%d shortgi=%d level=%d) tx=%dk/s)\n\r", + tx_cnt, tx_retry, succPro, tx_fail, rts_value, short_gi, level, tx_bps / 128); + bes2600_dbg(BES2600_DBG_TXRX_OPT, "txrx_opt: rx(cnt=%d rx=%dk/s) total=%dk/s\n\r", rx_cnt, rx_bps / 128, total_kbps); + bes2600_dbg(BES2600_DBG_TXRX_OPT, "txrx_opt: tx_delta_time=%d [%d %d %d %d] hw_value=%d ht=%d maxtxcnt=%d\n\r", + bes2600_get_tx_delta_time(), bes2600_get_tx_ac_delta_time(0), bes2600_get_tx_ac_delta_time(1), + bes2600_get_tx_ac_delta_time(2), bes2600_get_tx_ac_delta_time(3), priv->hw_value, + bes2600_station_is_ap_ht40(hw_priv), hw_priv->long_frame_max_tx_count); + + + /* dynamic set cca */ + bes2600_set_cca_method(hw_priv, priv, 0); + /* dynamic set agc */ + bes2600_set_dynamic_agc(hw_priv, priv, 0); +txrx_opt_clear: + bes2600_set_rts_threshold(priv->hw, rts_value); + bes2600_enable_tx_shortgi(hw_priv, priv, short_gi); + bes2600_set_high_edca_params(hw_priv, priv, level); + bes2600_clear_tx_delta_time(); + bes2600_clear_tx_ac_delta_time(0); + bes2600_clear_tx_ac_delta_time(1); + bes2600_clear_tx_ac_delta_time(2); + bes2600_clear_tx_ac_delta_time(3); + tx_bps = priv->dot11TransmittedDataBytes; + rx_bps = priv->dot11ReceivedDataBytes; + l_tx_cnt = priv->dot11TransmittedFrameCount; + l_tx_fail = priv->dot11FailedCount; + l_tx_retry = priv->dot11RetryCount; + l_rx_cnt = priv->dot11ReceivedFragmentCount; + return ; +} + +static struct bes2600_common *txrx_hw_priv = NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) +static void txrx_opt_timer_callback(struct timer_list* data) +#else +static void txrx_opt_timer_callback(unsigned long data) +#endif +{ + bes2600_dbg(BES2600_DBG_TXRX, "####Timer callback function Called time = %lu\n", jiffies); + queue_work(txrx_hw_priv->workqueue, &txrx_hw_priv->dynamic_opt_txrx_work); + mod_timer(&txrx_hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID)); +} + +static void txrx_opt_timer_start(struct bes2600_common *hw_priv) +{ + mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID)); +} + +static void txrx_opt_timer_stop(struct bes2600_common *hw_priv) +{ + del_timer_sync(&hw_priv->txrx_opt_timer); +} + +int bes2600_set_txrx_opt_default_param(struct bes2600_common * hw_priv) +{ + MIB_TXRX_OPT_PARAM g_txrx_param = {2, (PROCTECT_MODE_RTS_CTS | PROCTECT_MODE_RTS_CTS_RETRY), 2002}; + struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0); + if (priv == NULL) + return 0; + memcpy(&hw_priv->txrx_opt_param, &g_txrx_param, sizeof(MIB_TXRX_OPT_PARAM)); + bes2600_set_txrx_opt_param(hw_priv, priv, &hw_priv->txrx_opt_param); + return 0; +} + +int bes2600_set_txrx_opt_unjoin_param(struct bes2600_common * hw_priv) +{ + MIB_TXRX_OPT_PARAM g_txrx_param = {1, 0, 2002}; + struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0); + if (priv == NULL) + return 0; + memcpy(&hw_priv->txrx_opt_param, &g_txrx_param, sizeof(MIB_TXRX_OPT_PARAM)); + bes2600_set_txrx_opt_param(hw_priv, priv, &hw_priv->txrx_opt_param); + return 0; +} + +int txrx_opt_timer_init(struct bes2600_common *hw_priv) +{ + bes2600_info(BES2600_DBG_TXRX_OPT, "txrx_opt_timer_init:%p", txrx_hw_priv); + if (!txrx_hw_priv) { + txrx_hw_priv = hw_priv; + bes2600_info(BES2600_DBG_TXRX, "####Timer init hw_priv = %p\n", txrx_hw_priv); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + timer_setup(&hw_priv->txrx_opt_timer, txrx_opt_timer_callback, 0); +#else + init_timer(&hw_priv->txrx_opt_timer); + hw_priv->txrx_opt_timer.function = txrx_opt_timer_callback; + hw_priv->txrx_opt_timer.data = 0; +#endif + bes2600_set_txrx_opt_default_param(hw_priv); + } + + mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID)); + bes2600_pwr_register_en_lp_cb(hw_priv, txrx_opt_timer_stop); + bes2600_pwr_register_exit_lp_cb(hw_priv, txrx_opt_timer_start); + return 0; +} + +void txrx_opt_timer_exit(struct bes2600_common *hw_priv) +{ + bes2600_info(BES2600_DBG_TXRX_OPT, "txrx_opt_timer_exit"); + + del_timer_sync(&hw_priv->txrx_opt_timer); + cancel_work_sync(&hw_priv->dynamic_opt_txrx_work); + bes2600_pwr_unregister_en_lp_cb(hw_priv, txrx_opt_timer_stop); + bes2600_pwr_unregister_exit_lp_cb(hw_priv, txrx_opt_timer_start); + txrx_hw_priv = NULL; + bes2600_set_txrx_opt_unjoin_param(hw_priv); +} + + diff --git a/drivers/staging/bes2600/txrx_opt.h b/drivers/staging/bes2600/txrx_opt.h new file mode 100644 index 000000000000..582ff7fb5221 --- /dev/null +++ b/drivers/staging/bes2600/txrx_opt.h @@ -0,0 +1,31 @@ + +/*************************************************************************** + * + * Copyright 2015-2022 BES. + * All rights reserved. All unpublished rights reserved. + * + * No part of this work may be used or reproduced in any form or by any + * means, or stored in a database or retrieval system, without prior written + * permission of BES. + * + * Use of this work is governed by a license granted by BES. + * This work contains confidential and proprietary information of + * BES. which is protected by copyright, trade secret, + * trademark and other intellectual property rights. + * + ****************************************************************************/ +#ifndef bes2600_TXRX_OPT_H +#define bes2600_TXRX_OPT_H +#include +/* open it for enhance wifi throughput */ +#define BES2600_TX_RX_OPT 1 +void bes2600_add_tx_ac_delta_time(int ac, uint32_t del_time); +void bes2600_add_tx_delta_time(uint32_t tx_time); +void bes2600_rx_status(struct bes2600_vif *priv, struct sk_buff *skb); +void bes2600_tx_status(struct bes2600_vif *priv, struct sk_buff *skb); +void bes2600_dynamic_opt_rxtx(struct bes2600_common *hw_priv,struct bes2600_vif *priv, int rssi); +int txrx_opt_timer_init(struct bes2600_common *hw_priv); +int txrx_opt_timer_exit(struct bes2600_common *hw_priv); + +#endif + diff --git a/drivers/staging/bes2600/wifi_testmode_cmd.c b/drivers/staging/bes2600/wifi_testmode_cmd.c new file mode 100644 index 000000000000..3b3583a6db3d --- /dev/null +++ b/drivers/staging/bes2600/wifi_testmode_cmd.c @@ -0,0 +1,143 @@ +/* + * Mac80211 driver for BES2600 device + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifdef CONFIG_BES2600_TESTMODE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wsm.h" +#include "bes2600.h" +#include "bes_nl80211_testmode_msg.h" +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" +#include "bes2600_factory.h" + + +/* store the reply message of the rf cmd */ +static struct vendor_rf_cmd_msg_reply vendor_rf_cmd_reply; + +extern int bes2600_testmode_reply(struct wiphy *wiphy, const void *data, int len); + +void bes2600_rf_cmd_msg_assembly(u32 cmd_type, void *data, u32 msg_len) +{ + vendor_rf_cmd_reply.id = cmd_type; + vendor_rf_cmd_reply.msg_len = msg_len + 2 * sizeof(u32); + + if (msg_len) + memcpy(vendor_rf_cmd_reply.ret_msg, (u8 *)data, msg_len); + else + vendor_rf_cmd_reply.ret_msg[0] = '\0'; + +} + +/** + * bes2600_vendor_rf_cmd, signaling cmd & rf nosignaling cmd + * reaches bes2600 + * + * @hw: the hardware + * @vif: vif + * @data: incoming data + * @len: incoming data length + * + * Returns: 0 on success or non zero value on failure + */ +int bes2600_vendor_rf_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len) +{ + struct bes2600_common *hw_priv = hw->priv; + int ret = 0; + int recv_timeout = 1500; /*waiting for reply message*/ + struct vendor_rf_cmd_t *vendor_rf_cmd = (struct vendor_rf_cmd_t *)data; + //struct timeval tstart, tend; + int if_id = 0; + u32 count, cmd_len, cmd_type; + + count = vendor_rf_cmd->cmd_argc; + cmd_len = vendor_rf_cmd->cmd_len; + cmd_type = vendor_rf_cmd->cmd_type; + + if (count == 0 || cmd_len == 0 || + strlen(vendor_rf_cmd->cmd) + 1 != cmd_len) { + bes2600_err(BES2600_DBG_TEST_MODE, "%s line: %d, " + "vendor rf cmd parsing failed\n", __func__, __LINE__); + return -EINVAL; + } + + bes2600_info(BES2600_DBG_TEST_MODE, "vendor cmd = %s\n", vendor_rf_cmd->cmd); + + + /** + * signaling mode does not support the saving of calibration files, + * but the saved calibration values can be read. + * + */ + if (bes2600_chrdev_is_signal_mode()) { + if (cmd_type == VENDOR_RF_SAVE_FREQOFFSET_CMD || + cmd_type == VENDOR_RF_SAVE_POWERLEVEL_CMD || + cmd_type == VENDOR_RF_POWER_CALIB_FINISH) + return -EOPNOTSUPP; + } + + if (cmd_type == VENDOR_RF_SIG_NOSIG_MIX) { + if (bes2600_chrdev_is_signal_mode()) { + vendor_rf_cmd->cmd_type = VENDOR_RF_SIGNALING_CMD; + cmd_type = VENDOR_RF_SIGNALING_CMD; + } else { + vendor_rf_cmd->cmd_type = VENDOR_RF_NOSIGNALING_CMD; + cmd_type = VENDOR_RF_NOSIGNALING_CMD; + } + } + + switch (cmd_type) { + case VENDOR_RF_POWER_CALIB_FINISH: + case VENDOR_RF_GET_SAVE_FREQOFFSET_CMD: + case VENDOR_RF_GET_SAVE_POWERLEVEL_CMD: + case VENDOR_RF_SIGNALING_CMD: + case VENDOR_RF_NOSIGNALING_CMD: + case VENDOR_RF_SAVE_FREQOFFSET_CMD: + case VENDOR_RF_SAVE_POWERLEVEL_CMD: + sema_init(&hw_priv->vendor_rf_cmd_replay_sema, 0); + ret = wsm_vendor_rf_cmd(hw_priv, if_id, vendor_rf_cmd); + + if (ret) { + bes2600_err(BES2600_DBG_TEST_MODE, "vendor rf cmd send error code = %d\n", ret); + return ret; + } + + //do_gettimeofday(&tstart); + if (down_timeout(&hw_priv->vendor_rf_cmd_replay_sema, recv_timeout)) { + bes2600_err(BES2600_DBG_TEST_MODE, "vendor rf cmd failed to receive reply message\n"); + return -ENOMSG; + } + //do_gettimeofday(&tend); + //bes2600_dbg(BES2600_DBG_TEST_MODE,"recv time: %ldms\n", + //1000 * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec) / 1000); + + ret = bes2600_testmode_reply(hw->wiphy, &vendor_rf_cmd_reply, vendor_rf_cmd_reply.msg_len); + vendor_rf_cmd_reply.ret_msg[0] = '\0'; + break; + case VENDOR_RF_SIG_NOSIG_MIX: + ret = 0; + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} +#endif /* CONFIG_BES2600_TESTMODE */ diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c new file mode 100644 index 000000000000..8ba5ce0a0bb3 --- /dev/null +++ b/drivers/staging/bes2600/wsm.c @@ -0,0 +1,3132 @@ +/* + * WSM host interface (HI) implementation for + * BES2600 mac80211 drivers. + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "bes2600.h" +#include "wsm.h" +#include "bh.h" +#include "debug.h" +#include "itp.h" +#ifdef ROAM_OFFLOAD +#include "sta.h" +#endif /*ROAM_OFFLOAD*/ +#ifdef CONFIG_BES2600_TESTMODE +#include "bes_nl80211_testmode_msg.h" +#endif +#include "bes2600_driver_mode.h" +#include "bes_chardev.h" +#include "bes2600_factory.h" +#include "epta_coex.h" +#include "epta_request.h" + +#define WSM_CMD_TIMEOUT (6 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_CMD_RESET_TIMEOUT (7 * HZ) /* 2 sec. timeout was observed. */ +#define WSM_CMD_DEFAULT_TIMEOUT (7 * HZ) +#define WSM_SKIP(buf, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + goto underflow; \ + (buf)->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + goto underflow; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, cvt) \ + ({ \ + type val; \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + goto underflow; \ + val = cvt(*(type *)(buf)->data); \ + (buf)->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), size))) \ + goto nomem; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, cvt) \ + do { \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \ + goto nomem; \ + *(type *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); +static int get_interface_id_scanning(struct bes2600_common *hw_priv); + +static int wsm_cmd_send(struct bes2600_common *hw_priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo, int if_id); + +static struct bes2600_vif + *wsm_get_interface_for_tx(struct bes2600_common *hw_priv); + +static inline void wsm_cmd_lock(struct bes2600_common *hw_priv) +{ + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_TX); + down(&hw_priv->wsm_cmd_sema); +} + +static inline void wsm_cmd_unlock(struct bes2600_common *hw_priv) +{ + up(&hw_priv->wsm_cmd_sema); + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_TX); +} + +static inline void wsm_oper_lock(struct bes2600_common *hw_priv) +{ + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_OPER); + down(&hw_priv->wsm_oper_lock); +} + +static inline void wsm_oper_unlock(struct bes2600_common *hw_priv) +{ + up(&hw_priv->wsm_oper_lock); + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_OPER); +} + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct bes2600_common *hw_priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status)) + return -EINVAL; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_configuration(struct bes2600_common *hw_priv, + struct wsm_configuration *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(hw_priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct bes2600_common *hw_priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (WARN_ON(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (bes2600_chrdev_is_signal_mode()) { + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +//rf nosignaling tset +#ifdef CONFIG_BES2600_TESTMODE + +int wsm_vendor_rf_cmd_confirm(struct bes2600_common *hw_priv, void *arg, struct wsm_buf *buf) +{ + return 0; +} + +int wsm_vendor_rf_test_indication(struct bes2600_common *hw_priv, struct wsm_buf *buf) +{ + int i; + int16_t ret = 0; + u16 wsm_len; + u32 cmd_type; + struct wifi_power_cali_save_t power_cali_save; + struct wifi_freq_cali_t wifi_freq_cali; + struct wifi_get_power_cali_t power_cali_get; + struct wifi_power_cali_flag_t power_cali_flag; + struct wsm_mcu_hdr *msg_hdr = (struct wsm_mcu_hdr *)(buf->begin); + + wsm_len = __le16_to_cpu(msg_hdr->hdr.len); + cmd_type = __le32_to_cpu(msg_hdr->cmd_type); + buf->data += sizeof(struct wsm_mcu_hdr) - sizeof(struct wsm_hdr); + + switch (cmd_type) { + case VENDOR_RF_SAVE_FREQOFFSET_CMD: + case VENDOR_RF_GET_SAVE_FREQOFFSET_CMD: + wifi_freq_cali.save_type = WSM_GET16(buf); + wifi_freq_cali.freq_cali = WSM_GET16(buf); + wifi_freq_cali.status = -WSM_GET16(buf); + wifi_freq_cali.cali_flag = WSM_GET16(buf); + if (wifi_freq_cali.save_type == RF_CALIB_DATA_IN_LINUX) { + if (cmd_type == VENDOR_RF_SAVE_FREQOFFSET_CMD) { +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + ret = bes2600_wifi_cali_freq_write(&wifi_freq_cali); +#else + ret = -FACTORY_SAVE_FREQ_ERR; +#endif + } + + if (cmd_type == VENDOR_RF_GET_SAVE_FREQOFFSET_CMD) { +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + ret = vendor_get_freq_cali(&wifi_freq_cali); +#else + ret = -FACTORY_SAVE_FILE_NOT_EXIST; +#endif + } + wifi_freq_cali.status = ret; + } + bes2600_rf_cmd_msg_assembly(cmd_type, &wifi_freq_cali, + sizeof(struct wifi_freq_cali_t)); + break; + case VENDOR_RF_SAVE_POWERLEVEL_CMD: + power_cali_save.save_type = WSM_GET16(buf); + power_cali_save.mode = WSM_GET16(buf); + power_cali_save.bandwidth = WSM_GET16(buf); + power_cali_save.band = WSM_GET16(buf); + power_cali_save.ch = WSM_GET16(buf); + power_cali_save.power_cali = WSM_GET16(buf); + power_cali_save.status = -WSM_GET16(buf); + if (power_cali_save.save_type == RF_CALIB_DATA_IN_LINUX) { +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + ret = bes2600_wifi_power_cali_table_write(&power_cali_save); +#else + ret = -FACTORY_SAVE_POWER_ERR; +#endif + power_cali_save.status = ret; + } + bes2600_rf_cmd_msg_assembly(cmd_type, &power_cali_save, + sizeof(struct wifi_power_cali_save_t)); + break; + case VENDOR_RF_GET_SAVE_POWERLEVEL_CMD: + power_cali_get.save_type = WSM_GET16(buf); + if (power_cali_get.save_type == RF_CALIB_DATA_IN_LINUX) { +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + ret = vendor_get_power_cali(&power_cali_get); +#else + ret = -FACTORY_SAVE_FILE_NOT_EXIST; +#endif + power_cali_get.status = ret; + } else { + /* 2.4G have 3 cali ch */ + for (i = 0; i < 3; i++) + power_cali_get.tx_power_ch[i] = WSM_GET16(buf); + /* 5G have 13 cali ch */ + for (i = 0; i < 13; i++) + power_cali_get.tx_power_ch_5G[i] = WSM_GET16(buf); + power_cali_get.status = -WSM_GET16(buf); + } + bes2600_rf_cmd_msg_assembly(cmd_type, &power_cali_get, + sizeof(struct wifi_get_power_cali_t)); + + break; + case VENDOR_RF_POWER_CALIB_FINISH: + power_cali_flag.save_type = WSM_GET16(buf); + power_cali_flag.band = WSM_GET16(buf); + power_cali_flag.status = -WSM_GET16(buf); + if (power_cali_flag.save_type == RF_CALIB_DATA_IN_LINUX) { +#ifdef CONFIG_BES2600_CALIB_FROM_LINUX + ret = vendor_set_power_cali_flag(&power_cali_flag); +#else + ret = -FACTORY_SET_POWER_CALI_FLAG_ERR; +#endif + power_cali_flag.status = ret; + } + bes2600_rf_cmd_msg_assembly(cmd_type, &power_cali_flag, + sizeof(struct wifi_power_cali_flag_t)); + break; + case VENDOR_RF_SIGNALING_CMD: + case VENDOR_RF_NOSIGNALING_CMD: + bes2600_rf_cmd_msg_assembly(cmd_type, buf->data, wsm_len - sizeof(struct wsm_mcu_hdr)); + break; + default: + break; + } + + up(&hw_priv->vendor_rf_cmd_replay_sema); + + bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_TEST_CMD); + + return 0; + +underflow: + return -EINVAL; +} + +int wsm_vendor_rf_cmd(struct bes2600_common *hw_priv, int if_id, + const struct vendor_rf_cmd_t *vendor_rf_cmd) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* the command need to wait complete indication */ + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_TEST_CMD); + + WSM_PUT32(buf, vendor_rf_cmd->cmd_type); + WSM_PUT32(buf, vendor_rf_cmd->cmd_argc); + WSM_PUT32(buf, vendor_rf_cmd->cmd_len); + WSM_PUT(buf, vendor_rf_cmd->cmd, vendor_rf_cmd->cmd_len); + + /** + * vendor signaling and nosignaling use id 0x0C25. + */ + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C25, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} +#endif /* CONFIG_BES2600_TESTMODE */ + +/* ******************************************************************** */ + // wifi cpu sleep control + #ifdef BES_UNIFIED_PM +struct wsm_sleep_ctrl { + u16 msgid; + u16 msglen; + u32 disable; +}; + +int wsm_sleep_ctrl(struct bes2600_common *hw_priv, u32 disable, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + if (if_id != 0) { + WARN_ON(1); + return -EBUSY; + } + + wsm_cmd_lock(hw_priv); + WSM_PUT32(buf, disable); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0024, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} +#endif + +/* ******************************************************************** */ + +int wsm_reset(struct bes2600_common *hw_priv, const struct wsm_reset *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(hw_priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_read_mib(struct bes2600_common *hw_priv, u16 mibId, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(hw_priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT, -1); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct bes2600_common *hw_priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (WARN_ON(WSM_GET16(buf) != arg->mibId)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct bes2600_common *hw_priv, u16 mibId, void *_buf, + size_t buf_size, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(hw_priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +static int wsm_write_mib_confirm(struct bes2600_common *hw_priv, + struct wsm_mib *arg, + struct wsm_buf *buf, + int interface_link_id) +{ + int ret; + struct bes2600_vif *priv; + + if (!is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) + interface_link_id = 0; + + ret = wsm_generic_confirm(hw_priv, arg, buf); + if (ret) + return ret; + + if (arg->mibId == 0x1006) { + const char *p = arg->buf; + + /* Power save is enabled before add_interface is called */ + if (!hw_priv->vif_list[interface_link_id]) + return 0; + /* OperationalMode: update PM status. */ + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, + interface_link_id); + if (!priv) + return 0; + bes2600_enable_powersave(priv, + (p[0] & 0x0F) ? true : false); + spin_unlock(&priv->vif_lock); + } + return 0; +} + +/* ******************************************************************** */ + +int wsm_scan(struct bes2600_common *hw_priv, const struct wsm_scan *arg, + int if_id) +{ + int i; + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + if (unlikely(arg->numOfChannels > 48)) + return -EINVAL; + + if (unlikely(arg->numOfSSIDs > WSM_SCAN_MAX_NUM_OF_SSIDS)) + return -EINVAL; + + if (unlikely(arg->band > 1)) + return -EINVAL; + + wsm_oper_lock(hw_priv); + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->scanType); + WSM_PUT8(buf, arg->scanFlags); + WSM_PUT8(buf, arg->maxTransmitRate); + WSM_PUT32(buf, arg->autoScanInterval); + WSM_PUT8(buf, arg->numOfProbeRequests); + WSM_PUT8(buf, arg->numOfChannels); + WSM_PUT8(buf, arg->numOfSSIDs); + WSM_PUT8(buf, arg->probeDelay); + + for (i = 0; i < arg->numOfChannels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].minChannelTime); + WSM_PUT32(buf, arg->ch[i].maxChannelTime); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->numOfSSIDs; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + if (ret) + wsm_oper_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_oper_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct bes2600_common *hw_priv, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + wsm_cmd_lock(hw_priv); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; +} + + +static int wsm_tx_confirm(struct bes2600_common *hw_priv, + struct wsm_buf *buf, + int interface_link_id) +{ + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packetID = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.txedRate = WSM_GET8(buf); + tx_confirm.ackFailures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.mediaDelay = WSM_GET32(buf); + tx_confirm.txQueueDelay = WSM_GET32(buf); + + if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) { + /* TODO:COMBO:linkID will be stored in packetID*/ + /* TODO:COMBO: Extract traffic resumption map */ + tx_confirm.if_id = bes2600_queue_get_if_id(tx_confirm.packetID); + tx_confirm.link_id = bes2600_queue_get_link_id( + tx_confirm.packetID); + } else { + tx_confirm.link_id = interface_link_id; + tx_confirm.if_id = 0; + } + + wsm_release_vif_tx_buffer(hw_priv, tx_confirm.if_id, 1); + + if (hw_priv->wsm_cbc.tx_confirm) + hw_priv->wsm_cbc.tx_confirm(hw_priv, &tx_confirm); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_multi_tx_confirm(struct bes2600_common *hw_priv, + struct wsm_buf *buf, int interface_link_id) +{ + struct bes2600_vif *priv; + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (WARN_ON(count <= 0)) + return -EINVAL; + else if (count > 1) { + ret = wsm_release_tx_buffer(hw_priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + bes2600_bh_wakeup(hw_priv); + } + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, interface_link_id); + if (priv) { + bes2600_debug_txed_multi(priv, count); + spin_unlock(&priv->vif_lock); + } + for (i = 0; i < count; ++i) { + if(i < count - 1) + bes2600_bh_dec_pending_count(hw_priv, 0); + + ret = wsm_tx_confirm(hw_priv, buf, interface_link_id); + if (ret) + return ret; + } + return ret; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct bes2600_common *hw_priv, + struct wsm_join *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + + wsm_oper_unlock(hw_priv); + + if (status != WSM_STATUS_SUCCESS) { + bes2600_warn(BES2600_DBG_WSM, "wsm_join_confirm ret %u\n", status); + return -EINVAL; + } + + arg->minPowerLevel = WSM_GET32(buf); + arg->maxPowerLevel = WSM_GET32(buf); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_join(struct bes2600_common *hw_priv, struct wsm_join *arg, + int if_id) +/*TODO: combo: make it work per vif.*/ +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_oper_lock(hw_priv); + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atimWindow); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeForJoin); + WSM_PUT8(buf, arg->dtimPeriod); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssidLength); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT32(buf, arg->basicRateSet); + + hw_priv->tx_burst_idx = -1; + ret = wsm_cmd_send(hw_priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_oper_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct bes2600_common *hw_priv, + const struct wsm_set_bss_params *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->beaconLostCount); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operationalRateSet); + WSM_PUT32(buf, hw_priv->ht_info.ht_cap.mcs.rx_mask[0] << 14); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct bes2600_common *hw_priv, const struct wsm_add_key *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct bes2600_common *hw_priv, + const struct wsm_remove_key *arg, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->entryIndex); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct bes2600_common *hw_priv, + const struct wsm_set_tx_queue_params *arg, + u8 id, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, queue_id_to_wmm_aci[id]); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct bes2600_common *hw_priv, + const struct wsm_edca_params *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* Implemented according to specification. */ + + WSM_PUT16(buf, arg->params[3].cwMin); + WSM_PUT16(buf, arg->params[2].cwMin); + WSM_PUT16(buf, arg->params[1].cwMin); + WSM_PUT16(buf, arg->params[0].cwMin); + + WSM_PUT16(buf, arg->params[3].cwMax); + WSM_PUT16(buf, arg->params[2].cwMax); + WSM_PUT16(buf, arg->params[1].cwMax); + WSM_PUT16(buf, arg->params[0].cwMax); + + WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + + WSM_PUT16(buf, arg->params[3].txOpLimit); + WSM_PUT16(buf, arg->params[2].txOpLimit); + WSM_PUT16(buf, arg->params[1].txOpLimit); + WSM_PUT16(buf, arg->params[0].txOpLimit); + + WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct bes2600_common *hw_priv, + const struct wsm_switch_channel *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + wsm_lock_tx(hw_priv); + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->channelMode | 0x80); + WSM_PUT8(buf, arg->channelSwitchCount); + WSM_PUT16(buf, arg->newChannelNumber); + + hw_priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + if (ret) { + wsm_unlock_tx(hw_priv); + hw_priv->channel_switch_in_progress = 0; + } + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_unlock_tx(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct bes2600_common *hw_priv, const struct wsm_set_pm *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->pmMode); + WSM_PUT8(buf, arg->fastPsmIdlePeriod); + WSM_PUT8(buf, arg->apPsmChangePeriod); + WSM_PUT8(buf, arg->minAutoPsPollPeriod); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct bes2600_common *hw_priv, const struct wsm_start *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT32(buf, arg->CTWindow); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT8(buf, arg->DTIMPeriod); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeDelay); + WSM_PUT8(buf, arg->ssidLength); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basicRateSet); + + hw_priv->tx_burst_idx = -1; + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +#if 0 +/* This API is no longer present in WSC */ +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct bes2600_common *hw_priv, + const struct wsm_beacon_transmit *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} +#endif + +/* ******************************************************************** */ + +int wsm_start_find(struct bes2600_common *hw_priv, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct bes2600_common *hw_priv, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct bes2600_common *hw_priv, const struct wsm_map_link *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + u16 cmd = 0x001C; + + wsm_cmd_lock(hw_priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + + if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) { + WSM_PUT8(buf, arg->unmap); + WSM_PUT8(buf, arg->link_id); + } else { + cmd |= WSM_TX_LINK_ID(arg->link_id); + WSM_PUT16(buf, 0); + } + + ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_update_ie(struct bes2600_common *hw_priv, + const struct wsm_update_ie *arg, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; + +} + +int wsm_epta_cmd(struct bes2600_common *hw_priv, struct wsm_epta_msg *arg) +{ +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + static bool epta_lock_tx = false; + + if (arg->hw_epta_enable & (1 << 11)) + arg->hw_epta_enable &= ~(3 << 10); // force TDD + else if (coex_is_fdd_mode()) + arg->hw_epta_enable |= (1 << 10); //LMAC_COEX_MODE_FDD + + if (arg->hw_epta_enable != 3 || arg->hw_epta_enable != 4) { //use for wifi connect + ///TODO: remove this hack. use hardware in disconnect mode + if (coex_is_wifi_inactive()) { + arg->wlan_duration = 20000; + arg->bt_duration = 80000; + arg->hw_epta_enable &= ~(0x3); + } + // if (coex_is_fdd_mode()) { + // arg->wlan_duration = 100000; + // arg->bt_duration = 0; + // arg->hw_epta_enable |= (1 << 10); + // } else { + // if (coex_is_bt_inactive()) { + // arg->wlan_duration = 100000; + // arg->bt_duration = 0; + // arg->hw_epta_enable = 0; + // } + // } + } + + bes2600_info(BES2600_DBG_WSM, "epta cmd: wlan:%d bt:%d enable:%x", + arg->wlan_duration, arg->bt_duration, arg->hw_epta_enable); + + /* + Should lock tx queue to avoid frame stuck in firmware if wlan_duration is zero. + There is no need to distinguish epta mode as the duration reflects requirements of scenarios + */ + if (arg->wlan_duration == 0 && !epta_lock_tx) { + wsm_lock_tx(hw_priv); + epta_lock_tx = true; + } else if (epta_lock_tx && arg->wlan_duration != 0) { + wsm_unlock_tx(hw_priv); + epta_lock_tx = false; + } + + wsm_cmd_lock(hw_priv); + WSM_PUT32(buf, arg->wlan_duration); + WSM_PUT32(buf, arg->bt_duration); + WSM_PUT32(buf, arg->hw_epta_enable); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0029, WSM_CMD_TIMEOUT, 0); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +#else + return 0; +#endif +} + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE +int wsm_epta_wifi_chan_cmd(struct bes2600_common *hw_priv, uint32_t channel, uint32_t type) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* cmd type */ + WSM_PUT32(buf, BES2600_RF_CMD_CH_INFO); + + WSM_PUT32(buf, channel); + WSM_PUT16(buf, type); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, 0); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} +#endif + +int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* cmd type */ + WSM_PUT32(buf, BES2600_RF_CMD_WIFI_STATUS); + WSM_PUT32(buf, status); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, 0); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* cmd type */ + WSM_PUT32(buf, BES2600_RF_CMD_CPU_USAGE); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, 0); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +#ifdef PLAT_ALLWINNER_R329 +int wsm_save_factory_txt_to_flash(struct bes2600_common *hw_priv, const u8 *data, int if_id) +{ + int ret, i; + const struct factory_t *factory_cali = (const struct factory_t *)data; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* cmd type */ + WSM_PUT32(buf, BES2600_RF_CMD_CALI_TXT_TO_FLASH); + + WSM_PUT32(buf, factory_cali->data.iQ_offset); + WSM_PUT16(buf, factory_cali->data.freq_cal); + + for (i = 0; i < 3; i++) + WSM_PUT16(buf, factory_cali->data.tx_power_ch[i]); + + WSM_PUT8(buf, factory_cali->data.freq_cal_flags); + WSM_PUT8(buf, factory_cali->data.tx_power_type); + WSM_PUT16(buf, factory_cali->data.temperature); + + for (i = 0; i < 13; i++) + WSM_PUT16(buf, factory_cali->data.tx_power_ch_5G[i]); + + WSM_PUT16(buf, factory_cali->data.tx_power_flags_5G); + + for (i = 0; i < 4; i++) + WSM_PUT32(buf, factory_cali->data.bt_tx_power[i]); + + WSM_PUT16(buf, factory_cali->data.temperature_5G); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; + +} +#endif + +/* ******************************************************************** */ +#ifdef MCAST_FWDING +/* 3.66 */ +static int wsm_give_buffer_confirm(struct bes2600_common *hw_priv, + struct wsm_buf *buf) +{ + bes2600_dbg(BES2600_DBG_WSM, "[WSM] HW Buf count %d\n", hw_priv->hw_bufs_used); + if (!hw_priv->hw_bufs_used) + wake_up(&hw_priv->bh_evt_wq); + + return 0; +} + +/* 3.65 */ +int wsm_init_release_buffer_request(struct bes2600_common *hw_priv, u8 index) +{ + struct wsm_buf *buf = &hw_priv->wsm_release_buf[index]; + u16 cmd = 0x0022; /* Buffer Request */ + u8 flags; + size_t buf_len; + + wsm_buf_init(buf); + + flags = index ? 0: 0x1; + + WSM_PUT8(buf, flags); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + buf_len = buf->data - buf->begin; + + /* Fill HI message header */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + return 0; +nomem: + return -ENOMEM; +} + +/* 3.68 */ +static int wsm_request_buffer_confirm(struct bes2600_vif *priv, + u8 *arg, + struct wsm_buf *buf) +{ + u8 count; + u32 sta_asleep_mask = 0; + int i; + u32 mask = 0; + u32 change_mask = 0; + struct bes2600_common *hw_priv = priv->hw_priv; + + /* There is no status field in this message */ + sta_asleep_mask = WSM_GET32(buf); + count = WSM_GET8(buf); + count -= 1; /* Current workaround for FW issue */ + + spin_lock_bh(&priv->ps_state_lock); + change_mask = (priv->sta_asleep_mask ^ sta_asleep_mask); + bes2600_dbg(BES2600_DBG_WSM, "CM %x, HM %x, FWM %x\n", change_mask,priv->sta_asleep_mask, sta_asleep_mask); + spin_unlock_bh(&priv->ps_state_lock); + + if (change_mask) { + struct ieee80211_sta *sta; + int ret = 0; + + + for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE ; ++i) { + + if(BES2600_LINK_HARD != priv->link_id_db[i].status) + continue; + + mask = BIT(i + 1); + + /* If FW state and host state for this link are different then notify OMAC */ + if(change_mask & mask) { + + bes2600_dbg(BES2600_DBG_WSM, "PS State Changed %d for sta %pM\n", (sta_asleep_mask & mask) ? 1:0, priv->link_id_db[i].mac); + + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, priv->link_id_db[i].mac); + if (!sta) { + bes2600_err(BES2600_DBG_WSM, "[WSM] WRBC - could not find sta %pM\n", + priv->link_id_db[i].mac); + } else { + ret = ieee80211_sta_ps_transition_ni(sta, (sta_asleep_mask & mask) ? true: false); + bes2600_dbg(BES2600_DBG_WSM, "PS State NOTIFIED %d\n", ret); + WARN_ON(ret); + } + rcu_read_unlock(); + } + } + /* Replace STA mask with one reported by FW */ + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask = sta_asleep_mask; + spin_unlock_bh(&priv->ps_state_lock); + } + + bes2600_dbg(BES2600_DBG_WSM, "[WSM] WRBC - HW Buf count %d SleepMask %d\n", + hw_priv->hw_bufs_used, sta_asleep_mask); + hw_priv->buf_released = 0; + WARN_ON(count != (hw_priv->wsm_caps.numInpChBufs - 1)); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* 3.67 */ +int wsm_request_buffer_request(struct bes2600_vif *priv, + u8 *arg) +{ + int ret; + struct wsm_buf *buf = &priv->hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(priv->hw_priv); + + WSM_PUT8(buf, (*arg)); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv->hw_priv, buf, arg, 0x0023, WSM_CMD_JOIN_TIMEOUT,priv->if_id); + + wsm_cmd_unlock(priv->hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(priv->hw_priv); + return -ENOMEM; +} + +#endif + + +int wsm_set_keepalive_filter(struct bes2600_vif *priv, bool enable) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + priv->rx_filter.keepalive = enable; + return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); +} + +int wsm_set_probe_responder(struct bes2600_vif *priv, bool enable) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + + priv->rx_filter.probeResponder = enable; + return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); +} +/* ******************************************************************** */ +/* WSM indication events implementation */ + +static int wsm_startup_indication(struct bes2600_common *hw_priv, + struct wsm_buf *buf) +{ + u16 status; + char fw_label[129]; + static const char * const fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" + }; + + hw_priv->wsm_caps.numInpChBufs = WSM_GET16(buf); + hw_priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf); + hw_priv->wsm_caps.hardwareId = WSM_GET16(buf); + hw_priv->wsm_caps.hardwareSubId = WSM_GET16(buf); + status = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareCap = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareType = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareApiVer = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareVersion = WSM_GET16(buf); + WSM_GET(buf, &fw_label[0], sizeof(fw_label) - 1); + fw_label[sizeof(fw_label) - 1] = 0; /* Do not trust FW too much. */ + + if (WARN_ON(status)) + return -EINVAL; + + if (WARN_ON(hw_priv->wsm_caps.firmwareType > 4)) + return -EINVAL; + + bes2600_dbg(BES2600_DBG_INIT, "BES2600 WSM init done.\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware [%s], ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + hw_priv->wsm_caps.numInpChBufs, + hw_priv->wsm_caps.sizeInpChBuf, + hw_priv->wsm_caps.hardwareId, + hw_priv->wsm_caps.hardwareSubId, + fw_types[hw_priv->wsm_caps.firmwareType], + &fw_label[0], + hw_priv->wsm_caps.firmwareVersion, + hw_priv->wsm_caps.firmwareBuildNumber, + hw_priv->wsm_caps.firmwareApiVer, + hw_priv->wsm_caps.firmwareCap); + + hw_priv->wsm_caps.firmwareReady = 1; + + wake_up(&hw_priv->wsm_startup_done); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_receive_indication(struct bes2600_common *hw_priv, + int interface_link_id, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + struct bes2600_vif *priv; + + hw_priv->rx_timestamp = jiffies; + if (hw_priv->wsm_cbc.rx) { + struct wsm_rx rx; + struct ieee80211_hdr *hdr; + size_t hdr_len; + __le16 fctl; + + rx.status = WSM_GET32(buf); + rx.channelNumber = WSM_GET16(buf); + rx.rxedRate = WSM_GET8(buf); + rx.rcpiRssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + buf->data+=16; // 2019-10-25, for lmac wsm data ind struct update + + /* TODO:COMBO: Frames received from scanning are received + * with interface ID == 2 */ + if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) { + if (interface_link_id == CW12XX_GENERIC_IF_ID) { + /* Frames received in response to SCAN + * Request */ + interface_link_id = + get_interface_id_scanning(hw_priv); + if (interface_link_id == -1) { + interface_link_id = hw_priv->roc_if_id; + } +#ifdef ROAM_OFFLOAD + if (hw_priv->auto_scanning) { + interface_link_id = hw_priv->scan.if_id; + } +#endif/*ROAM_OFFLOAD*/ + } + /* linkid (peer sta id is encoded in bit 25-28 of + flags field */ + rx.link_id = ((rx.flags & (0xf << 25)) >> 25); + rx.if_id = interface_link_id; + } else { + rx.link_id = interface_link_id; + rx.if_id = 0; + } + + if(rx.if_id == -1) { + bes2600_info(BES2600_DBG_WSM, "%s: intf is not match\n", __func__); + return 0; + } + + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, rx.if_id); + if (!priv) { + bes2600_info(BES2600_DBG_WSM, "%s: NULL priv drop frame\n", __func__); + return 0; + } + + /* FW Workaround: Drop probe resp or + beacon when RSSI is 0 */ + hdr = (struct ieee80211_hdr *) buf->data; + + if (!rx.rcpiRssi && + (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control))) { + spin_unlock(&priv->vif_lock); + return 0; + } + + /* If no RSSI subscription has been made, + * convert RCPI to RSSI here */ + if (!priv->cqm_use_rssi) + rx.rcpiRssi = rx.rcpiRssi / 2 - 110; + + fctl = *(__le16 *)buf->data; + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + + if (!rx.status && + unlikely(ieee80211_is_deauth(fctl) || ieee80211_is_disassoc(fctl))) { + bes2600_dbg(BES2600_DBG_WSM, "rx deauth or disassoc, priv->join_status:%u", priv->join_status); + if (priv->join_status == BES2600_JOIN_STATUS_STA && + (ether_addr_equal(hdr->addr3, priv->join_bssid) || + ether_addr_equal(hdr->addr3, priv->bssid))) { + bool ignore = false; + + if (is_multicast_ether_addr(hdr->addr1)) { + struct ieee80211_mmie *mmie; + bool has_mmie = false; + /* + * We may receive broadcast/multicast robust management frame + * when PMF is enabled, in that case, FW has checked the MMIE + * and verified IPN and MIC. + * However, some buggy AP, e.g. Tenda AC6, send broadcast deauth + * with MMIE even when no management frame protection, + * this frame should be ignored and dropped by mac80211. + */ + if ((*skb_p)->len >= 24 + sizeof(*mmie)) { + mmie = (struct ieee80211_mmie *) + ((*skb_p)->data + (*skb_p)->len - sizeof(*mmie)); + if (mmie->element_id == WLAN_EID_MMIE && + mmie->length == sizeof(*mmie) - 2) + has_mmie = true; + } + bes2600_info(BES2600_DBG_WSM, "[WSM] RX broadcast/multicast deauth: len=%d, has_mmie:%u, pmf=%d\n", + (*skb_p)->len, has_mmie, priv->pmf); + if (has_mmie ^ priv->pmf) + ignore = true; + } else if (ether_addr_equal(hdr->addr1, hw_priv->addresses[rx.if_id].addr)) { + bool has_protected = ieee80211_has_protected(fctl); + bes2600_info(BES2600_DBG_WSM, "[WSM] RX unicast deauth: protected=%d, pmf=%d, connect_in_process=%d\n", + has_protected, priv->pmf, atomic_read(&priv->connect_in_process)); + /* + * We should report unprotected deauth to mac80211 for + * SA query when PMF is enabled, so cannot unjoin here. + * If PMF is disabled, it is unexpected to receive + * a protected unicast deauth. + */ + if ((has_protected ^ priv->pmf) || atomic_read(&priv->connect_in_process)) + ignore = true; + } else { + ignore = true; + } + + if (!ignore) { + /* Schedule unjoin work */ + bes2600_info(BES2600_DBG_WSM, "[WSM] Issue unjoin command (RX).\n"); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } + } + hw_priv->wsm_cbc.rx(priv, &rx, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + spin_unlock(&priv->vif_lock); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct bes2600_common *hw_priv, + struct wsm_buf *buf, + int interface_link_id) +{ + int first; + struct bes2600_wsm_event *event; + struct bes2600_vif *priv; + + if (!is_hardware_cw1250(hw_priv) && !is_hardware_cw1260(hw_priv)) + interface_link_id = 0; + + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, interface_link_id); + + if (unlikely(!priv)) { + bes2600_info(BES2600_DBG_WSM, "[WSM] Not find corresponding interface\n"); + return 0; + } + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + spin_unlock(&priv->vif_lock); + return 0; + } + spin_unlock(&priv->vif_lock); + + event = kzalloc(sizeof(struct bes2600_wsm_event), GFP_KERNEL); + + event->evt.eventId = __le32_to_cpu(WSM_GET32(buf)); + event->evt.eventData = __le32_to_cpu(WSM_GET32(buf)); + event->if_id = interface_link_id; + + bes2600_dbg(BES2600_DBG_WSM, "[WSM] Event: %d(%d)\n", + event->evt.eventId, event->evt.eventData); + + spin_lock(&hw_priv->event_queue_lock); + first = list_empty(&hw_priv->event_queue); + list_add_tail(&event->link, &hw_priv->event_queue); + spin_unlock(&hw_priv->event_queue_lock); + + if (first) + queue_work(hw_priv->workqueue, &hw_priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +/* TODO:COMBO:Make this perVIFF once mac80211 support is available */ +static int wsm_channel_switch_indication(struct bes2600_common *hw_priv, + struct wsm_buf *buf) +{ + wsm_unlock_tx(hw_priv); /* Re-enable datapath */ + WARN_ON(WSM_GET32(buf)); + + hw_priv->channel_switch_in_progress = 0; + wake_up(&hw_priv->channel_switch_done); + + if (hw_priv->wsm_cbc.channel_switch) + hw_priv->wsm_cbc.channel_switch(hw_priv); + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct bes2600_common *hw_priv, + struct wsm_buf *buf) +{ + struct wsm_set_pm_complete arg; + + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + + if(arg.status == WSM_STATUS_SUCCESS) { + bes2600_pwr_notify_ps_changed(hw_priv, arg.psm); + } else { + bes2600_err(BES2600_DBG_WSM, "[WSM] PM Ind status:%d psm:%d\n", arg.status, arg.psm); + } + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_scan_complete_indication(struct bes2600_common *hw_priv, + struct wsm_buf *buf) +{ +#ifdef ROAM_OFFLOAD + if(hw_priv->auto_scanning == 0) + wsm_oper_unlock(hw_priv); +#else + wsm_oper_unlock(hw_priv); +#endif /*ROAM_OFFLOAD*/ + + if (hw_priv->wsm_cbc.scan_complete) { + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.numChannels = WSM_GET8(buf); + hw_priv->wsm_cbc.scan_complete(hw_priv, &arg); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct bes2600_common *hw_priv, + struct wsm_buf *buf) +{ + /* TODO: Implement me. */ + //STUB(); + return 0; +} + +static int wsm_suspend_resume_indication(struct bes2600_common *hw_priv, + int interface_link_id, + struct wsm_buf *buf) +{ + if (hw_priv->wsm_cbc.suspend_resume) { + u32 flags; + struct wsm_suspend_resume arg; + struct bes2600_vif *priv; + + if (is_hardware_cw1250(hw_priv) || + is_hardware_cw1260(hw_priv)) { + int i; + arg.if_id = interface_link_id; + /* TODO:COMBO: Extract bitmap from suspend-resume + * TX indication */ + bes2600_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if (priv->join_status == + BES2600_JOIN_STATUS_AP) { + arg.if_id = priv->if_id; + break; + } + arg.link_id = 0; + } + } else { + arg.if_id = 0; + arg.link_id = interface_link_id; + } + + flags = WSM_GET32(buf); + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, arg.if_id); + if (unlikely(!priv)) { + bes2600_dbg(BES2600_DBG_WSM, "[WSM] suspend-resume indication" + " for removed interface!\n"); + return 0; + } + hw_priv->wsm_cbc.suspend_resume(priv, &arg); + spin_unlock(&priv->vif_lock); + } + return 0; + +underflow: + return -EINVAL; +} + +/** + * signaling cmd confirm. + */ + +int wsm_driver_rf_cmd_confirm(struct bes2600_common *hw_priv, void *arg, struct wsm_buf *buf) +{ + int ret = 0; + u32 cmd_type; + struct wsm_mcu_hdr *msg_hdr = (struct wsm_mcu_hdr *)(buf->begin); + + cmd_type = __le32_to_cpu(msg_hdr->cmd_type); + buf->data += sizeof(struct wsm_mcu_hdr) - sizeof(struct wsm_hdr); + + switch (cmd_type) { + case BES2600_RF_CMD_CALI_TXT_TO_FLASH: + ret = WSM_GET32(buf); + break; + default: + break; + } + + return ret; + +underflow: + return -EINVAL; +} + +/* ******************************************************************** */ +/* WSM TX */ + +int wsm_cmd_send(struct bes2600_common *hw_priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo, int if_id) +{ + size_t buf_len = buf->data - buf->begin; + int ret; + + if (cmd == 0x0006) /* Write MIB */ + bes2600_dbg(BES2600_DBG_WSM, "[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%lu)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + (long unsigned)buf_len); + else + bes2600_dbg(BES2600_DBG_WSM, "[WSM] >>> 0x%.4X (%lu)\n", cmd, (long unsigned)buf_len); + + /* Fill HI message header */ + /* BH will add sequence number */ + + /* TODO:COMBO: Add if_id from to the WSM header */ + /* if_id == -1 indicates that command is HW specific, + * eg. wsm_configuration which is called during driver initialzation + * (mac80211 .start callback called when first ifce is created. )*/ + + /* send hw specific commands on if 0 */ + if (if_id == -1) + if_id = 0; + + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + if (IS_DRIVER_TO_MCU_CMD(cmd)) + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + else + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd | + ((is_hardware_cw1250(hw_priv)|| is_hardware_cw1260(hw_priv)) ? + (if_id << 6) : 0)); + +#ifdef BES2600_HOST_TIMESTAMP_DEBUG + if (buf->end >= buf->data + 4) + *(u32 *)buf->data = (u32)jiffies_to_msecs(jiffies); +#endif + + spin_lock(&hw_priv->wsm_cmd.lock); + BUG_ON(hw_priv->wsm_cmd.ptr); + hw_priv->wsm_cmd.done = 0; + hw_priv->wsm_cmd.ptr = buf->begin; + hw_priv->wsm_cmd.len = buf_len; + hw_priv->wsm_cmd.arg = arg; + hw_priv->wsm_cmd.cmd = cmd; + spin_unlock(&hw_priv->wsm_cmd.lock); + bes2600_tx_loop_record_wsm_cmd(hw_priv, hw_priv->wsm_cmd.ptr); + + bes2600_bh_wakeup(hw_priv); + + if (unlikely(hw_priv->bh_error)) { + /* Do not wait for timeout if BH is dead. Exit immediately. */ + ret = 0; + } else { + long rx_timestamp; + long wsm_cmd_starttime = jiffies; + long wsm_cmd_runtime; + long wsm_cmd_max_tmo = WSM_CMD_DEFAULT_TIMEOUT; + + /* Give start cmd a little more time */ + if (tmo == WSM_CMD_START_TIMEOUT) + wsm_cmd_max_tmo = WSM_CMD_START_TIMEOUT; + /* Firmware prioritizes data traffic over control confirm. + * Loop below checks if data was RXed and increases timeout + * accordingly. */ + do { + /* It's safe to use unprotected access to + * wsm_cmd.done here */ + ret = wait_event_timeout( + hw_priv->wsm_cmd_wq, + hw_priv->wsm_cmd.done, tmo); + rx_timestamp = jiffies - hw_priv->rx_timestamp; + wsm_cmd_runtime = jiffies - wsm_cmd_starttime; + if (unlikely(rx_timestamp < 0) || wsm_cmd_runtime < 0) + rx_timestamp = tmo + 1; + } while (!ret && rx_timestamp <= tmo && + wsm_cmd_runtime < wsm_cmd_max_tmo); + } + + if (unlikely(ret == 0)) { + u16 raceCheck; + + spin_lock(&hw_priv->wsm_cmd.lock); + raceCheck = hw_priv->wsm_cmd.cmd; + hw_priv->wsm_cmd.arg = NULL; + hw_priv->wsm_cmd.ptr = NULL; + spin_unlock(&hw_priv->wsm_cmd.lock); + + /* Race condition check to make sure _confirm is not called + * after exit of _send */ + if (raceCheck == 0xFFFF) { + /* If wsm_handle_rx got stuck in _confirm we will hang + * system there. It's better than silently currupt + * stack or heap, isn't it? */ + BUG_ON(wait_event_timeout( + hw_priv->wsm_cmd_wq, + hw_priv->wsm_cmd.done, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + } + + /* Kill BH thread to report the error to the top layer. */ + //hw_priv->bh_error = 1; + wake_up(&hw_priv->bh_wq); + ret = -ETIMEDOUT; + } else { + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.arg = NULL; + hw_priv->wsm_cmd.ptr = NULL; + BUG_ON(!hw_priv->wsm_cmd.done); + ret = hw_priv->wsm_cmd.ret; + spin_unlock(&hw_priv->wsm_cmd.lock); + } + bes2600_tx_loop_clear_wsm_cmd(hw_priv); + wsm_buf_reset(buf); + return ret; +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct bes2600_common *hw_priv) +{ + wsm_cmd_lock(hw_priv); + if (atomic_add_return(1, &hw_priv->tx_lock) == 1) { + if (wsm_flush_tx(hw_priv)) + bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is locked.\n"); + } + wsm_cmd_unlock(hw_priv); +} + +void wsm_vif_lock_tx(struct bes2600_vif *priv) +{ + struct bes2600_common *hw_priv = priv->hw_priv; + + wsm_cmd_lock(hw_priv); + if (atomic_add_return(1, &hw_priv->tx_lock) == 1) { + if (wsm_vif_flush_tx(priv)) + bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is locked for" + " if_id %d.\n", priv->if_id); + } + wsm_cmd_unlock(hw_priv); +} + +void wsm_lock_tx_async(struct bes2600_common *hw_priv) +{ + if (atomic_add_return(1, &hw_priv->tx_lock) == 1) + bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is locked (async).\n"); +} + +bool wsm_flush_tx(struct bes2600_common *hw_priv) +{ + unsigned long timestamp = jiffies; + bool pending = false; + long timeout; + int i; + + /* Flush must be called with TX lock held. */ + BUG_ON(!atomic_read(&hw_priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. */ + + if (!hw_priv->hw_bufs_used) + return true; + + if (hw_priv->bh_error) { + /* In case of failure do not wait for magic. */ + bes2600_err(BES2600_DBG_WSM, "[WSM] Fatal error occured, " + "will not flush TX.\n"); + return false; + } else { + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= bes2600_queue_get_xmit_timestamp( + &hw_priv->tx_queue[i], + ×tamp, CW12XX_ALL_IFS, + 0xffffffff); + /* It is allowed to lock TX with only a command in the pipe. */ + if (!pending) + return true; + + timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (timeout < 0 || wait_event_timeout(hw_priv->bh_evt_wq, + !hw_priv->hw_bufs_used, + timeout) <= 0) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + bes2600_chrdev_wifi_force_close(hw_priv, true); + } + + /* Ok, everything is flushed. */ + return true; + } +} + +bool wsm_vif_flush_tx(struct bes2600_vif *priv) +{ + struct bes2600_common *hw_priv = priv->hw_priv; + unsigned long timestamp = jiffies; + long timeout; + int i; + int if_id = priv->if_id; + + + /* Flush must be called with TX lock held. */ + BUG_ON(!atomic_read(&hw_priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. */ + + if (!hw_priv->hw_bufs_used_vif[priv->if_id]) + return true; + + if (hw_priv->bh_error) { + /* In case of failure do not wait for magic. */ + bes2600_err(BES2600_DBG_WSM, "[WSM] Fatal error occured, " + "will not flush TX.\n"); + return false; + } else { + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + bes2600_queue_get_xmit_timestamp( + &hw_priv->tx_queue[i], + ×tamp, if_id, + 0xffffffff); + /* It is allowed to lock TX with only a command in the pipe. */ + if (!hw_priv->hw_bufs_used_vif[if_id]) + return true; + + timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (timeout < 0 || wait_event_timeout(hw_priv->bh_evt_wq, + !hw_priv->hw_bufs_used_vif[if_id], + timeout) <= 0) { + + /* Hmmm... Not good. Frame had stuck in firmware. */ + bes2600_chrdev_wifi_force_close(hw_priv, true); + } + + /* Ok, everything is flushed. */ + return true; + } +} + + +void wsm_unlock_tx(struct bes2600_common *hw_priv) +{ + int tx_lock; + if (hw_priv->bh_error) + bes2600_err(BES2600_DBG_WSM, "fatal error occured, unlock is unsafe\n"); + else { + tx_lock = atomic_sub_return(1, &hw_priv->tx_lock); + if (tx_lock < 0) { + BUG_ON(1); + } else if (tx_lock == 0) { + bes2600_bh_wakeup(hw_priv); + bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is unlocked.\n"); + } + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct bes2600_common *hw_priv, u8 *data, size_t len) +{ +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + struct bes2600_vif *priv = NULL; + int if_id = 0; +#endif + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + size_t i; + + static const char * const reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; + +#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) + /* Send the event upwards on the FW exception */ + bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ); + + spin_lock(&hw_priv->vif_list_lock); + bes2600_for_each_vif(hw_priv, priv, if_id) { + if (!priv) + continue; + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + } + spin_unlock(&hw_priv->vif_list_lock); +#endif + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) + wiphy_err(hw_priv->hw->wiphy, + "Firmware exception: %s.\n", + reason_str[reason]); + else + wiphy_err(hw_priv->hw->wiphy, + "Firmware assert at %.*s, line %d\n", + (int)sizeof(fname), fname, (int)reg[1]); + + for (i = 0; i < 12; i += 4) + wiphy_err(hw_priv->hw->wiphy, + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + (int)i + 0, reg[i + 0], (int)i + 1, reg[i + 1], + (int)i + 2, reg[i + 2], (int)i + 3, reg[i + 3]); + wiphy_err(hw_priv->hw->wiphy, + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wiphy_err(hw_priv->hw->wiphy, + "CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, + fname, sizeof(fname)); + return 0; + +underflow: + wiphy_err(hw_priv->hw->wiphy, + "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, + data, len); + return -EINVAL; +} +EXPORT_SYMBOL(wsm_handle_exception); + +int wsm_bt_ts_request(struct bes2600_common *hw_priv, struct wsm_buf *buf) +{ +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + uint32_t type; + + type = __le32_to_cpu(((struct wsm_mcu_hdr *)(buf->begin))->cmd_type); + bbt_change_current_status(hw_priv, type); + return 0; +#else + return 0; +#endif +} + +int wsm_handle_rx(struct bes2600_common *hw_priv, int id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + struct wsm_buf wsm_buf; +// struct bes2600_vif *priv = NULL; +// int i = 0; + int interface_link_id = (id >> 6) & 0x0F; + u32 ind_confirm_label = 0x0; /* wsm to mcu cmd ind & cnfirm label */ + +#ifdef ROAM_OFFLOAD +#if 0 + struct bes2600_vif *priv; + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, interface_link_id); + if (unlikely(!priv)) { + WARN_ON(1); + return 0; + } + spin_unlock(&priv->vif_lock); +#endif +#endif/*ROAM_OFFLOAD*/ + + /* Strip link id. */ + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; + + bes2600_dbg(BES2600_DBG_WSM, "[WSM] <<< 0x%.4X (%ld)\n", id, + (long)(wsm_buf.end - wsm_buf.begin)); + + if (IS_DRIVER_TO_MCU_CMD(id)) + ind_confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label); + + if (id == 0x0C30) { + ret = wsm_bt_ts_request(hw_priv, &wsm_buf); + } else if (id == 0x404) { + ret = wsm_tx_confirm(hw_priv, &wsm_buf, interface_link_id); +#ifdef MCAST_FWDING +#if 1 + } else if (id == 0x422) { + ret = wsm_give_buffer_confirm(hw_priv, &wsm_buf); +#endif +#endif + + } else if (id == 0x41E) { + ret = wsm_multi_tx_confirm(hw_priv, &wsm_buf, + interface_link_id); + } else if (WSM_CONFIRM_CONDITION(id, ind_confirm_label)) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). */ + spin_lock(&hw_priv->wsm_cmd.lock); + wsm_arg = hw_priv->wsm_cmd.arg; + wsm_cmd = hw_priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + hw_priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock(&hw_priv->wsm_cmd.lock); + + if (((id & 0x0f00) == 0x0400) && WARN_ON((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + switch (id) { + case 0x0409: + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). */ + if (likely(wsm_arg)) + ret = wsm_configuration_confirm(hw_priv, + wsm_arg, + &wsm_buf); + break; + case 0x0405: + if (likely(wsm_arg)) + ret = wsm_read_mib_confirm(hw_priv, wsm_arg, + &wsm_buf); + break; + case 0x0406: + if (likely(wsm_arg)) + ret = wsm_write_mib_confirm(hw_priv, wsm_arg, + &wsm_buf, + interface_link_id); + break; + case 0x040B: + if (likely(wsm_arg)) + ret = wsm_join_confirm(hw_priv, wsm_arg, + &wsm_buf); + break; + +#ifdef MCAST_FWDING + case 0x0423: /* req buffer cfm*/ + if (likely(wsm_arg)){ + bes2600_for_each_vif(hw_priv, priv, i) { + if (priv && (priv->join_status == BES2600_JOIN_STATUS_AP)) + ret = wsm_request_buffer_confirm(priv, + wsm_arg, &wsm_buf); + } + } + break; +#endif + case 0x0407: /* start-scan */ +#ifdef ROAM_OFFLOAD + if (hw_priv->auto_scanning) { + if (atomic_read(&hw_priv->scan.in_progress)) { + hw_priv->auto_scanning = 0; + } + else { + wsm_oper_unlock(hw_priv); + up(&hw_priv->scan.lock); + } + } +#endif /*ROAM_OFFLOAD*/ + case 0x0408: /* stop-scan */ + case 0x040A: /* wsm_reset */ + case 0x040C: /* add_key */ + case 0x040D: /* remove_key */ + case 0x0410: /* wsm_set_pm */ + case 0x0411: /* set_bss_params */ + case 0x0412: /* set_tx_queue_params */ + case 0x0413: /* set_edca_params */ + case 0x0416: /* switch_channel */ + case 0x0417: /* start */ + case 0x0418: /* beacon_transmit */ + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ + case 0x041C: /* map_link */ + case 0x0429: /* epta */ + WARN_ON(wsm_arg != NULL); + ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); + if (ret) + wiphy_warn(hw_priv->hw->wiphy, + "wsm_generic_confirm " + "failed for request 0x%.4X.\n", + id & ~0x0400); + break; +#ifdef CONFIG_BES2600_TESTMODE + case 0x0C25: + ret = wsm_vendor_rf_cmd_confirm(hw_priv, wsm_arg, &wsm_buf); + break; + case 0x0C27: + ret = wsm_driver_rf_cmd_confirm(hw_priv, wsm_arg, &wsm_buf); + break; +#endif /* CONFIG_BES2600_TESTMODE */ +#ifdef BES_UNIFIED_PM + case 0x0424: /* wifi sleep disable */ + break; +#endif + default: + BUG_ON(1); + } + + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.ret = ret; + hw_priv->wsm_cmd.done = 1; + spin_unlock(&hw_priv->wsm_cmd.lock); + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up(&hw_priv->wsm_cmd_wq); + } else if ((id & 0x0f00) == 0x0800) { + switch (id) { + case 0x0801: + ret = wsm_startup_indication(hw_priv, &wsm_buf); + break; + case 0x0804: + ret = wsm_receive_indication(hw_priv, interface_link_id, + &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(hw_priv, &wsm_buf, + interface_link_id); + break; + case 0x080A: + ret = wsm_channel_switch_indication(hw_priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(hw_priv, &wsm_buf); + break; + case 0x0806: +#ifdef ROAM_OFFLOAD + if(hw_priv->auto_scanning && hw_priv->frame_rcvd) { + struct bes2600_vif *priv; + hw_priv->frame_rcvd = 0; + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + if (unlikely(!priv)) { + WARN_ON(1); + return 0; + } + spin_unlock(&priv->vif_lock); + if (hw_priv->beacon) { + struct wsm_scan_complete *scan_cmpl = \ + (struct wsm_scan_complete *) \ + ((u8 *)wsm + sizeof(struct wsm_hdr)); + struct ieee80211_rx_status *rhdr = \ + IEEE80211_SKB_RXCB(hw_priv->beacon); + rhdr->signal = (s8)scan_cmpl->reserved; + if (!priv->cqm_use_rssi) { + rhdr->signal = rhdr->signal / 2 - 110; + } + if (!hw_priv->beacon_bkp) + hw_priv->beacon_bkp = \ + skb_copy(hw_priv->beacon, GFP_ATOMIC); + ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); + hw_priv->beacon = hw_priv->beacon_bkp; + + hw_priv->beacon_bkp = NULL; + } + bes2600_dbg(BES2600_DBG_WSM, "[WSM] Send Testmode Event.\n"); +#ifdef CONFIG_BES2600_TESTMODE + bes2600_testmode_event(priv->hw->wiphy, + BES_MSG_NEW_SCAN_RESULTS, 0, + 0, GFP_KERNEL); +#endif + + } +#endif /*ROAM_OFFLOAD*/ + ret = wsm_scan_complete_indication(hw_priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(hw_priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(hw_priv, + interface_link_id, &wsm_buf); + break; + default: + //STUB(); + break; + } + } else if (WSM_TO_MCU_CMD_IND_CONDITION(id, ind_confirm_label)) { + switch (id) { +#ifdef CONFIG_BES2600_TESTMODE + case 0x0C25: + ret = wsm_vendor_rf_test_indication(hw_priv, &wsm_buf); + break; +#endif /* CONFIG_BES2600_TESTMODE */ + default: + break; + } + } else { + WARN_ON(1); + ret = -EINVAL; + } +out: + return ret; +} +EXPORT_SYMBOL(wsm_handle_rx); + +static bool wsm_handle_tx_data(struct bes2600_vif *priv, + const struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info, + struct bes2600_txpriv *txpriv, + struct bes2600_queue *queue) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); +#ifdef P2P_MULTIVIF + struct bes2600_vif *p2p_if_vif = NULL; +#endif + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; + __le16 fctl = frame->frame_control; + enum { + doProbe, + doDrop, + doJoin, + doOffchannel, + doWep, + doTx, + } action = doTx; + + hw_priv = cw12xx_vifpriv_to_hwpriv(priv); +#ifdef P2P_MULTIVIF + if (priv->if_id == CW12XX_GENERIC_IF_ID) + p2p_if_vif = __cw12xx_hwpriv_to_vifpriv(hw_priv, 2); +#endif + frame = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; + fctl = frame->frame_control; + + switch (priv->mode) { + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_STATION: + if (unlikely((priv->join_status == BES2600_JOIN_STATUS_STA) && + ieee80211_is_nullfunc(fctl))) { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status == BES2600_BSS_LOSS_CHECKING) { + priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMING; + priv->bss_loss_confirm_id = wsm->packetID; + } + spin_unlock(&priv->bss_loss_lock); + } else if (unlikely( + (priv->join_status <= BES2600_JOIN_STATUS_MONITOR) || + memcmp(frame->addr1, priv->join_bssid, + sizeof(priv->join_bssid)))) { +#ifdef P2P_MULTIVIF + if (p2p_if_vif && (p2p_if_vif->join_status > + BES2600_JOIN_STATUS_MONITOR) + && (priv->join_status + < BES2600_JOIN_STATUS_MONITOR)) { + /* + * Post group formation, frame transmission on p2p0 + * interafce should not use offchannel/generic channel. + * Instead, the frame should be transmitted on interafce + * 1. This is needed by wsc fw. + */ + action = doTx; + txpriv->raw_if_id = 0; + } else +#endif + if (ieee80211_is_auth(fctl)) + action = doJoin; + else if (ieee80211_is_probe_req(fctl)) + action = doTx; + else if (memcmp(frame->addr1, priv->join_bssid, + sizeof(priv->join_bssid)) && + (priv->join_status == + BES2600_JOIN_STATUS_STA) && + (ieee80211_is_data(fctl))) { + action = doDrop; + } + else if (priv->join_status >= + BES2600_JOIN_STATUS_MONITOR) + action = doTx; + else if (get_interface_id_scanning(hw_priv) != -1) { + wiphy_warn(priv->hw->wiphy, + "Scan ONGOING dropping offchannel" + " eligible frame.\n"); + action = doDrop; + } + else + action = doTx; + } + break; + case NL80211_IFTYPE_AP: + if (unlikely(!priv->join_status)) + action = doDrop; + else if (unlikely(!(BIT(txpriv->raw_link_id) & + (BIT(0) | priv->link_id_map)))) { + wiphy_warn(priv->hw->wiphy, + "A frame with expired link id " + "is dropped.\n"); + action = doDrop; + } + if (bes2600_queue_get_generation(wsm->packetID) > + BES2600_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. */ + wiphy_warn(priv->hw->wiphy, + "Too many attempts " + "to requeue a frame. " + "Frame is dropped.\n"); + action = doDrop; + } + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + //STUB(); + case NL80211_IFTYPE_MONITOR: + default: + action = doDrop; + break; + } + + if (action == doTx) { + if (unlikely(ieee80211_is_probe_req(fctl))) { +#ifdef CONFIG_BES2600_TESTMODE + if (hw_priv->enable_advance_scan && + (priv->join_status == BES2600_JOIN_STATUS_STA) && + (hw_priv->advanceScanElems.scanMode == + BES2600_SCAN_MEASUREMENT_ACTIVE)) + /* If Advance Scan is Requested on Active Scan + * then transmit the Probe Request */ + action = doTx; + else +#endif + action = doProbe; + } else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && + tx_info->control.hw_key && + unlikely(tx_info->control.hw_key->keyidx != + priv->wep_default_key_id) && + (tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP104)) + action = doWep; + } + + switch (action) { + case doProbe: + { + /* An interesting FW "feature". Device filters + * probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. */ + bes2600_dbg(BES2600_DBG_WSM, + "[WSM] Convert probe request to scan.\n"); + wsm_lock_tx_async(hw_priv); + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + queue_delayed_work(hw_priv->workqueue, + &hw_priv->scan.probe_work, 0); + handled = true; + } + break; + case doDrop: + { + /* See detailed description of "join" below. + * We are dropping everything except AUTH in non-joined mode. */ + bes2600_err(BES2600_DBG_WSM, "[WSM] Drop frame (0x%.4X).\n", fctl); +#ifdef CONFIG_BES2600_TESTMODE + BUG_ON(bes2600_queue_remove(hw_priv, queue, + __le32_to_cpu(wsm->packetID))); +#else + BUG_ON(bes2600_queue_remove(queue, + __le32_to_cpu(wsm->packetID))); +#endif /*CONFIG_BES2600_TESTMODE*/ + handled = true; + } + break; + case doJoin: + { + /* There is one more interesting "feature" + * in FW: it can't do RX/TX before "join". + * "Join" here is not an association, + * but just a syncronization between AP and STA. + * priv->join_status is used only in bh thread and does + * not require protection */ + bes2600_info(BES2600_DBG_WSM, "[WSM] Issue join command.\n"); + wsm_lock_tx_async(hw_priv); + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if (hw_priv->channel->band != NL80211_BAND_2GHZ) +#else + if (hw_priv->channel->band != IEEE80211_BAND_2GHZ) +#endif + bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTING_5G); + else + bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTING); +#endif + if (queue_work(hw_priv->workqueue, &priv->join_work) <= 0) + wsm_unlock_tx(hw_priv); + handled = true; + } + break; + case doOffchannel: + { + bes2600_info(BES2600_DBG_WSM, "[WSM] Offchannel TX request.\n"); + wsm_lock_tx_async(hw_priv); + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(hw_priv->workqueue, &priv->offchannel_work) <= 0) + wsm_unlock_tx(hw_priv); + handled = true; + } + break; + case doWep: + { + bes2600_info(BES2600_DBG_WSM, "[WSM] Issue set_default_wep_key.\n"); + wsm_lock_tx_async(hw_priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(hw_priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(hw_priv); + handled = true; + } + break; + case doTx: + { +#if 0 + /* Kept for history. If you want to implement wsm->more, + * make sure you are able to send a frame after that. */ + wsm->more = (count > 1) ? 1 : 0; + if (wsm->more) { + /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * It's undocumented in WSM spec, but BES2600 hangs + * if 'more' is set and no TX is performed due to TX + * buffers limitation. */ + if (priv->hw_bufs_used + 1 == + priv->wsm_caps.numInpChBufs) + wsm->more = 0; + } + + /* BUG!!! FIXME: we can't use 'more' at all: we don't know + * future. It could be a request from upper layer with TX lock + * requirements (scan, for example). If "more" is set device + * will not send data and wsm_tx_lock() will fail... + * It's not obvious how to fix this deadlock. Any ideas? + * As a workaround more is set to 0. */ + wsm->more = 0; +#endif /* 0 */ + + if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + /* Shedule unjoin work */ + bes2600_info(BES2600_DBG_WSM, "[WSM] Issue unjoin command (TX).\n"); + atomic_set(&priv->connect_in_process, 0); +#if 0 + wsm->more = 0; +#endif /* 0 */ + +#ifdef WIFI_BT_COEXIST_EPTA_ENABLE + bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTING); +#endif + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } + break; + } + return handled; +} + +static int bes2600_get_prio_queue(struct bes2600_vif *priv, + u32 link_id_map, int *total) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + static u32 urgent; + struct wsm_edca_queue_params *edca; + unsigned score, best = -1; + int winner = -1; + int queued; + int i; + urgent = BIT(priv->link_id_after_dtim) | BIT(priv->link_id_uapsd); + + /* search for a winner using edca params */ + for (i = 0; i < 4; ++i) { + queued = bes2600_queue_get_num_queued(priv, + &hw_priv->tx_queue[i], + link_id_map); + if (!queued) + continue; + *total += queued; + edca = &priv->edca.params[i]; + score = ((edca->aifns + edca->cwMin) << 16) + + (edca->cwMax - edca->cwMin) * + (get_random_int() & 0xFFFF); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && hw_priv->tx_burst_idx >= 0 && + winner != hw_priv->tx_burst_idx && + !bes2600_queue_get_num_queued(priv, + &hw_priv->tx_queue[winner], + link_id_map & urgent) && + bes2600_queue_get_num_queued(priv, + &hw_priv->tx_queue[hw_priv->tx_burst_idx], + link_id_map)) + winner = hw_priv->tx_burst_idx; + + return winner; +} + +static int wsm_get_tx_queue_and_mask(struct bes2600_vif *priv, + struct bes2600_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv); + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->tx_multicast) { + tx_allowed_mask = BIT(priv->link_id_after_dtim); + idx = bes2600_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(priv->link_id_uapsd); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(priv->link_id_after_dtim); + } else { + tx_allowed_mask |= BIT(priv->link_id_after_dtim); + } + idx = bes2600_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &hw_priv->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +int wsm_get_tx(struct bes2600_common *hw_priv, u8 **data, + size_t *tx_len, int *burst, int *vif_selected) +{ + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct bes2600_queue *queue = NULL; + int queue_num; + u32 tx_allowed_mask = 0; + struct bes2600_txpriv *txpriv = NULL; +#ifdef P2P_MULTIVIF + int first = 1; +#endif + /* + * Count was intended as an input for wsm->more flag. + * During implementation it was found that wsm->more + * is not usable, see details above. It is kept just + * in case you would like to try to implement it again. + */ + int count = 0; +#ifdef P2P_MULTIVIF + int if_pending = CW12XX_MAX_VIFS - 1; +#else + int if_pending = 1; +#endif + + /* More is used only for broadcasts. */ + bool more = false; + + count = bes2600_itp_get_tx(hw_priv, data, tx_len, burst); + if (count) + return count; + + if (hw_priv->wsm_cmd.ptr) { + ++count; + spin_lock(&hw_priv->wsm_cmd.lock); + BUG_ON(!hw_priv->wsm_cmd.ptr); + *data = hw_priv->wsm_cmd.ptr; + *tx_len = hw_priv->wsm_cmd.len; + *burst = 1; + *vif_selected = -1; + spin_unlock(&hw_priv->wsm_cmd.lock); + } else { + for (;;) { + int ret; + struct bes2600_vif *priv; +#if 0 + int num_pending_vif0, num_pending_vif1; +#endif + if (atomic_add_return(0, &hw_priv->tx_lock)) + break; + /* Keep one buffer reserved for commands. Note + that, hw_bufs_used has already been incremented + before reaching here. */ + if (hw_priv->hw_bufs_used >= + hw_priv->wsm_caps.numInpChBufs) + break; +#ifdef P2P_MULTIVIF + if (first) { + first = 0; + hw_priv->if_id_selected = 0; + } +#endif + priv = wsm_get_interface_for_tx(hw_priv); + /* go to next interface ID to select next packet */ +#ifdef P2P_MULTIVIF + hw_priv->if_id_selected++; + if(hw_priv->if_id_selected > 2) + hw_priv->if_id_selected = 0; +#else + hw_priv->if_id_selected ^= 1; +#endif + + /* There might be no interface before add_interface + * call */ + if (!priv) { + if (if_pending) { +#ifdef P2P_MULTIVIF + if_pending--; +#else + if_pending = 0; +#endif + continue; + } + break; + } + +#if 0 + if (((priv->if_id == 0) && + (hw_priv->hw_bufs_used_vif[0] >= + CW12XX_FW_VIF0_THROTTLE)) || + ((priv->if_id == 1) && + (hw_priv->hw_bufs_used_vif[1] >= + CW12XX_FW_VIF1_THROTTLE))) { + spin_unlock(&priv->vif_lock); + if (if_pending) { + if_pending = 0; + continue; + } + break; + } +#endif + + /* This can be removed probably: bes2600_vif will not + * be in hw_priv->vif_list (as returned from + * wsm_get_interface_for_tx) until it's fully + * enabled, so statement above will take case of that*/ + if (!atomic_read(&priv->enabled)) { + spin_unlock(&priv->vif_lock); + break; + } + + /* TODO:COMBO: Find the next interface for which + * packet needs to be found */ + spin_lock_bh(&priv->ps_state_lock); + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + queue_num = queue - hw_priv->tx_queue; + + if (priv->buffered_multicasts && + (ret || !more) && + (priv->tx_multicast || + !priv->sta_asleep_mask)) { + priv->buffered_multicasts = false; + if (priv->tx_multicast) { + priv->tx_multicast = false; + queue_work(hw_priv->workqueue, + &priv->multicast_stop_work); + } + } + + spin_unlock_bh(&priv->ps_state_lock); + + if (ret) { + spin_unlock(&priv->vif_lock); +#ifdef P2P_MULTIVIF + if (if_pending) { +#else + if (if_pending == 1) { +#endif +#ifdef P2P_MULTIVIF + if_pending--; +#else + if_pending = 0; +#endif + continue; + } + break; + } + + if (bes2600_queue_get(queue, + priv->if_id, + tx_allowed_mask, + &wsm, &tx_info, &txpriv)) { + spin_unlock(&priv->vif_lock); + if_pending = 0; + continue; + } +#ifndef P2P_MULTIVIF + { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + + bes2600_dbg(BES2600_DBG_WSM, "QGET-1 %x, off_id %d," + " if_id %d\n", + hdr->frame_control, + txpriv->offchannel_if_id, + priv->if_id); + } +#endif + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv, queue)) { + spin_unlock(&priv->vif_lock); + if_pending = 0; + continue; /* Handled by WSM */ + } + + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_IF_ID(WSM_TX_IF_ID_MAX)); +#ifdef P2P_MULTIVIF + if (txpriv->raw_if_id) + wsm->hdr.id |= cpu_to_le16( + WSM_TX_IF_ID(txpriv->raw_if_id)); +#else + if (txpriv->offchannel_if_id) + wsm->hdr.id |= cpu_to_le16( + WSM_TX_IF_ID(txpriv->offchannel_if_id)); +#endif + else + wsm->hdr.id |= cpu_to_le16( + WSM_TX_IF_ID(priv->if_id)); + + *vif_selected = priv->if_id; +#ifdef ROC_DEBUG + { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + + bes2600_dbg(BES2600_DBG_WSM, "QGET-2 %x, off_id %d," + " if_id %d\n", + hdr->frame_control, + txpriv->offchannel_if_id, + priv->if_id); + } +#endif + + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + + /* allow bursting if txop is set */ + if (priv->edca.params[queue_num].txOpLimit) + *burst = min(*burst, + (int)bes2600_queue_get_num_queued(priv, + queue, tx_allowed_mask) + 1); + else + *burst = 1; + + /* store index of bursting queue */ + if (*burst > 1) + hw_priv->tx_burst_idx = queue_num; + else + hw_priv->tx_burst_idx = -1; + + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + if(strstr(&priv->ssid[0], "6.1.12")) { + if(hdr->addr1[0] & 0x01 ) { + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + } + else { + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + } + bes2600_dbg(BES2600_DBG_WSM, "[WSM] >>> 0x%.4X (%lu) %p %c\n", + 0x0004, (long unsigned)*tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + spin_unlock(&priv->vif_lock); + break; + } + } + + return count; +} + +void wsm_txed(struct bes2600_common *hw_priv, u8 *data) +{ + if (data == hw_priv->wsm_cmd.ptr) { + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.ptr = NULL; + spin_unlock(&hw_priv->wsm_cmd.lock); + } else { + bes2600_pwr_set_busy_event_async(hw_priv, BES_PWR_LOCK_ON_LMAC_RSP); + } +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + BUG_ON(buf->begin); + buf->begin = kmalloc(SDIO_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + buf->end = buf->begin ? &buf->begin[SDIO_BLOCK_SIZE] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else + buf->data = buf->begin; +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + + if (size & (SDIO_BLOCK_SIZE - 1)) { + size &= SDIO_BLOCK_SIZE; + size += SDIO_BLOCK_SIZE; + } + + buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} + +static struct bes2600_vif + *wsm_get_interface_for_tx(struct bes2600_common *hw_priv) +{ + struct bes2600_vif *priv = NULL, *i_priv; + int i = hw_priv->if_id_selected; + + if (is_hardware_cw1250(hw_priv) || 1 /*TODO:COMBO*/) { + spin_lock(&hw_priv->vif_list_lock); +#if 0 + bes2600_for_each_vif(hw_priv, i_priv, i) { + if (i_priv) { + priv = i_priv; + spin_lock(&priv->vif_lock); + break; + } + } +#endif + i_priv = hw_priv->vif_list[i] ? + cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[i]) : NULL; + if (i_priv) { + priv = i_priv; + spin_lock(&priv->vif_lock); + } + /* TODO:COMBO: + * Find next interface based on TX bitmap announced by the FW + * Find next interface based on load balancing */ + spin_unlock(&hw_priv->vif_list_lock); + } else { + priv = cw12xx_hwpriv_to_vifpriv(hw_priv, 0); + } + + return priv; +} + +static inline int get_interface_id_scanning(struct bes2600_common *hw_priv) +{ + if (hw_priv->scan.req) + return hw_priv->scan.if_id; + else + return -1; +} diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h new file mode 100644 index 000000000000..5bb5bace57ff --- /dev/null +++ b/drivers/staging/bes2600/wsm.h @@ -0,0 +1,2211 @@ +/* + * WSM host interface (HI) interface for BES2600 mac80211 drivers + * + * Copyright (c) 2022, Bestechnic + * Author: + * + * Based on BES2600 UMAC WSM API, which is + * Copyright (C) SA 2010 + * Author: Stewart Mathers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BES2600_WSM_H_INCLUDED +#define BES2600_WSM_H_INCLUDED + +#include + +struct bes2600_common; +struct bes2600_vif; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_5 (2) */ + +/* 11 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_11 (3) */ + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +#define WSM_FLAG_MAC_INSTANCE_1 (BIT(4)) + +#define WSM_FLAG_MAC_INSTANCE_0 (~(BIT(4))) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) +#ifdef CONFIG_BES2600_TESTMODE +/* Transmit flags */ +/* Start Expiry time from the receipt of tx request */ +#define WSM_TX_FLAG_EXPIRY_TIME (BIT(0)) +#endif /*CONFIG_BES2600_TESTMODE*/ + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS BIT(0) + +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN BIT(1) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* DECRYPTED */ +#define WSM_RX_STATUS_DECRYPTED (7<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) +#define WSM_KEY_TYPE_IGTK_GROUP (8) + +/* Key indexes */ +#define CW1250_WSM_KEY_MAX_INDEX (15) +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + +/* PS Mode Error */ +#define WSM_PS_ERROR_NO_ERROR 0 +#define WSM_PS_ERROR_AP_NOT_RESP_TO_POLL 1 +#define WSM_PS_ERROR_AP_NOT_RESP_TO_UAPSD_TRIGGER 2 +#define WSM_PS_ERROR_AP_SENT_UNICAST_IN_DOZE 3 +#define WSM_PS_ERROR_AP_NO_DATA_AFTER_TIM 4 + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +#define WSM_EVENT_PS_MODE_ERROR (7) + +#define WSM_EVENT_INACTIVITY (9) + +#define WSM_EVENT_WAKEUP_EVENT (10) + +/* MAC Addr Filter */ +#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030 + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C + +/* This is the end of specification. */ + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 4.38 ProtectedMgmtPolicy */ +#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 + +/* 4.39 SetHtProtection */ +#define WSM_MID_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Inactivity */ +#define WSM_MIB_ID_SET_INACTIVITY 0x1035 + +/* MAC Addr Filter */ +#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030 + +/* IP Addr Filter */ +#define WSM_MIB_ID_IPV4_ADDR_FILTERING 0x1031 + +#ifdef MCAST_FWDING +/* 4.51 Set Forwarding Offload */ +#define WSM_MIB_ID_FORWARDING_OFFLOAD 0x1033 +#endif + +#define WSM_MIB_ID_EXT_BASE 0x1040 +#define WSM_MIB_ID_EXT_RF_ENABLE (WSM_MIB_ID_EXT_BASE + 0) /* Mib to set RF on/off */ +#define WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME (WSM_MIB_ID_EXT_BASE + 1) /* Mib to set tcp keep alive frame */ +#define WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD (WSM_MIB_ID_EXT_BASE + 2) /* Mib to set tcp keep alive period */ +#define WSM_MIB_ID_EXT_TX_SHORT_GI_ENABLED (WSM_MIB_ID_EXT_BASE + 3) /* Mib to set tx short enabled */ +#define WSM_MIB_ID_EXT_TXRX_OPT_PARAM (WSM_MIB_ID_EXT_BASE + 4) /* Mib to set tx/rx opt param to enhance wifi throuhput */ + + +#ifdef IPV6_FILTERING +/* IpV6 Addr Filter */ +/* 4.52 Neighbor solicitation IPv6 address table */ +#define WSM_MIB_IP_IPV6_ADDR_FILTER 0x1032 +#define WSM_MIB_ID_NS_IP_ADDRESSES_TABLE 0x1034 +#define WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES 1 +#endif /*IPV6_FILTERING*/ + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) +#define WSM_FRAME_TYPE_ARP_REPLY (6) + +#ifdef IPV6_FILTERING +#define WSM_FRAME_TYPE_NA (7) +#endif /*IPV6_FILTERING*/ + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + +/* Advanced filtering options */ +#define WSM_MAX_FILTER_ELEMENTS (4) + +#define WSM_FILTER_ACTION_IGNORE (0) +#define WSM_FILTER_ACTION_FILTER_OUT (1) +#define WSM_FILTER_ACTION_FILTER_IN (2) + +#define WSM_FILTER_ADDR_MODE_NONE (0) +#define WSM_FILTER_ADDR_MODE_A1 (1) +#define WSM_FILTER_ADDR_MODE_A2 (2) +#define WSM_FILTER_ADDR_MODE_A3 (3) + +#define WSM_FILTER_PORT_TYPE_DST (0) +#define WSM_FILTER_PORT_TYPE_SRC (1) + + + +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +/* mcu -> driver confirm or indication hdr */ +struct wsm_mcu_hdr { + struct wsm_hdr hdr; + uint32_t m_reserve; /* reserved member variable */ + uint32_t handle_label; /* confirm or indication */ + uint32_t cmd_type; +}; + +#define WSM_MSG_ID_MASK (0x0C3F) +#define WSM_MSG_ID_GET(x) ((x) & WSM_MSG_ID_MASK) +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_MSG_SEQ_GET(x) (((x) >> 13) & WSM_TX_SEQ_MAX) +#define WSM_BES2600_CMD_ID_LABLE (0x0C00) +#define IS_DRIVER_TO_MCU_CMD(id) ((id & WSM_BES2600_CMD_ID_LABLE) == WSM_BES2600_CMD_ID_LABLE) +#define WSM_TXRX_SEQ_IDX(id) \ + (((id & 0xF00) == WSM_BES2600_CMD_ID_LABLE) ? 1 : 0) +#define WSM_TO_MCU_CMD_CONFIRM_LABEL (0x0f) +#define WSM_TO_MCU_CMD_INDICATION_LABEL (0xf0) + +#define WSM_CONFIRM_CONDITION(id, confirm_label) (((id & 0x0f00) == 0x0400 || \ + (((id & 0x0f00) == WSM_BES2600_CMD_ID_LABLE) && confirm_label == WSM_TO_MCU_CMD_CONFIRM_LABEL))) + +#define WSM_TO_MCU_CMD_IND_CONDITION(id, ind_label) (((id & 0x0f00) == WSM_BES2600_CMD_ID_LABLE) && \ + ind_label == WSM_TO_MCU_CMD_INDICATION_LABEL) + +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + +#define WSM_TX_IF_ID_MAX (0x0F) +#define WSM_TX_IF_ID(if_id) \ + ((if_id & WSM_TX_IF_ID_MAX) << 6) + +#define MAX_BEACON_SKIP_TIME_MS 1000 + +#ifdef FPGA_SETUP +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 9 / 2) +#else +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 20 / 2) +#endif +#define WSM_CMD_EXTENDED_TIMEOUT (HZ * 20 / 2) + +#define WSM_RI_GET_PEER_ID_FROM_FLAGS(_f) (((_f)&(0xF<<25)>>25)) + + +/* ******************************************************************** */ +/* WSM capcbility */ + +struct wsm_caps { + u16 numInpChBufs; + u16 sizeInpChBuf; + u16 hardwareId; + u16 hardwareSubId; + u16 firmwareCap; + u16 firmwareType; + u16 firmwareApiVer; + u16 firmwareBuildNumber; + u16 firmwareVersion; + int firmwareReady; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +/* 3.1 */ +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct bes2600_common *hw_priv, + struct wsm_configuration *arg, + int if_id); + +/* 3.3 */ +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct bes2600_common *hw_priv, const struct wsm_reset *arg, + int if_id); + +/* 3.5 */ +int wsm_read_mib(struct bes2600_common *hw_priv, u16 mibId, void *buf, + size_t buf_size); + +/* 3.7 */ +int wsm_write_mib(struct bes2600_common *hw_priv, u16 mibId, void *buf, + size_t buf_size, int if_id); + +/* 3.9 */ +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 minChannelTime; + u32 maxChannelTime; + u32 txPowerLevel; +}; + +/* 3.13 */ +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 numChannels; +#ifdef ROAM_OFFLOAD + u16 reserved; +#endif /*ROAM_OFFLOAD*/ +}; + +typedef void (*wsm_scan_complete_cb) (struct bes2600_common *hw_priv, + struct wsm_scan_complete *arg); + +/* 3.9 */ +struct wsm_scan { + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* WSM_SCAN_TYPE_... */ + /* [in] */ u8 scanType; + + /* WSM_SCAN_FLAG_... */ + /* [in] */ u8 scanFlags; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTransmitRate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + /* [in] */ u32 autoScanInterval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + /* [in] */ u32 numOfProbeRequests; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + /* [in] */ u8 numOfChannels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + /* [in] */ u8 numOfSSIDs; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + /* [in] */ struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + /* [in] */ struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct bes2600_common *hw_priv, const struct wsm_scan *arg, + int if_id); + +/* 3.11 */ +int wsm_stop_scan(struct bes2600_common *hw_priv, int if_id); + +/* 3.14 */ +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + /* [out] */ u32 packetID; + + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 txedRate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + /* [out] */ u8 ackFailures; + + /* WSM_TX_STATUS_... */ + /* [out] */ u16 flags; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + /* [out] */ u32 mediaDelay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + /* [out] */ u32 txQueueDelay; + + /* [out]*/ u32 link_id; + + /*[out]*/ int if_id; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct bes2600_common *hw_priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in bes2600_tx, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: bes2600_tx must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + /* [in/wsm] */ struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + /* [in] */ __le32 packetID; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTxRate; + + /* WSM_QUEUE_... */ + /* [in] */ u8 queueId; + + /* True: another packet is pending on the host for transmission. */ + /* [wsm] */ u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + /* [in] */ u8 flags; + + /* Should be 0. */ + /* [in] */ __le32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + /* [wsm] */ __le32 expireTime; + + /* WSM_HT_TX_... */ + /* [in] */ __le32 htTxParameters; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +struct wsm_rx { + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* Specifies the channel of the received packet. */ + /* [out] */ u16 channelNumber; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 rxedRate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + /* [out] */ u8 rcpiRssi; + + /* WSM_RX_STATUS_... */ + /* [out] */ u32 flags; + + /* An 802.11 frame. */ + /* [out] */ void *frame; + + /* Size of the frame */ + /* [out] */ size_t frame_size; + + /* Link ID */ + /* [out] */ int link_id; + /* [out] */ int if_id; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct bes2600_vif *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 eventId; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 eventData; +}; + +struct bes2600_wsm_event { + struct list_head link; + struct wsm_event evt; + u8 if_id; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct bes2600_common *hw_priv, + struct wsm_event *arg); + +/* 3.23 */ +struct wsm_join { + /* WSM_JOIN_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + /* [in] */ u16 channelNumber; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + /* [in] */ u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + /* [in] */ u16 atimWindow; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + /* [in] */ u8 probeForJoin; + + /* DTIM Period (In multiples of beacon interval) */ + /* [in] */ u8 dtimPeriod; + + /* WSM_JOIN_FLAGS_... */ + /* [in] */ u8 flags; + + /* Length of the SSID */ + /* [in] */ u32 ssidLength; + + /* Specifies the SSID of the IBSS to join or start */ + /* [in] */ u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + /* [in] */ u32 beaconInterval; + + /* A bit mask that defines the BSS basic rate set. */ + /* [in] */ u32 basicRateSet; + + /* Minimum transmission power level in units of 0.1dBm */ + /* [out] */ int minPowerLevel; + + /* Maximum transmission power level in units of 0.1dBm */ + /* [out] */ int maxPowerLevel; +}; + +int wsm_join(struct bes2600_common *hw_priv, struct wsm_join *arg, int if_id); + +/* 3.25 */ +struct wsm_set_pm { + /* WSM_PSM_... */ + /* [in] */ u8 pmMode; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 fastPsmIdlePeriod; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 apPsmChangePeriod; + + /* in unit of 500us; 0 to disable auto-pspoll */ + /* [in] */ u8 minAutoPsPollPeriod; +}; + +int wsm_set_pm(struct bes2600_common *hw_priv, const struct wsm_set_pm *arg, + int if_id); + + +/* 3.27 */ +struct wsm_set_pm_complete { + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* WSM_PSM_... */ + /* [out] */u8 psm; +}; + +typedef void (*wsm_set_pm_complete_cb) (struct bes2600_common *hw_priv, + struct wsm_set_pm_complete *arg); + +/* 3.28 */ +struct wsm_set_bss_params { + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beaconLostCount; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operationalRateSet; +}; + +int wsm_set_bss_params(struct bes2600_common *hw_priv, + const struct wsm_set_bss_params *arg, int if_id); + +/* 3.30 */ +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 entryIndex; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved; + u8 keyLength; /* Key length in bytes */ + u8 keyData[16]; /* Key data */ + } __packed wepPairwiseKey; + struct { + u8 keyId; /* Unique per key identifier + * (0..3) */ + u8 keyLength; /* Key length in bytes */ + u16 reserved; + u8 keyData[16]; /* Key data */ + } __packed wepGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved[2]; + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 txMicKey[8]; /* Tx MIC key */ + } __packed tkipPairwiseKey; + struct { + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed tkipGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u16 reserved; + u8 aesKeyData[16]; /* AES key data */ + } __packed aesPairwiseKey; + struct { + u8 aesKeyData[16]; /* AES key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed aesGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 keyId; /* Key ID */ + u8 reserved; + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + } __packed wapiPairwiseKey; + struct { + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + } __packed wapiGroupKey; + struct { + u8 IGTKKeyData[16]; + u8 keyId; + u8 Reserved[3]; + u8 IPN[8]; /* actual IPN is only 6 bytes long */ + } __packed igtkGroupKey; + } __packed; +} __packed; + +int wsm_add_key(struct bes2600_common *hw_priv, const struct wsm_add_key *arg, + int if_id); + +/* 3.32 */ +struct wsm_remove_key { + /* Key entry index : 0-10 */ + u8 entryIndex; +}; + +int wsm_remove_key(struct bes2600_common *hw_priv, + const struct wsm_remove_key *arg, int if_id); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +struct wsm_tx_queue_params { + /* NOTE: index is a linux queue id. */ + struct wsm_set_tx_queue_params params[4]; +}; + +#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time, \ + max_life_time) \ +do { \ + struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ + p->ackPolicy = (ack_policy); \ + p->allowedMediumTime = (allowed_time); \ + p->maxTransmitLifetime = (max_life_time); \ +} while (0) + +int wsm_set_tx_queue_params(struct bes2600_common *hw_priv, + const struct wsm_set_tx_queue_params *arg, + u8 id, int if_id); + +/* 3.36 */ +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + /* [in] */ u16 cwMin; + + /* CWmax (in slots) for the access class. */ + /* [in] */ u16 cwMax; + + /* AIFS (in slots) for the access class. */ + /* [in] */ u8 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + /* [in] */ u16 txOpLimit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + /* [in] */ u32 maxReceiveLifetime; + + /* UAPSD trigger support for the access class. */ + /* [in] */ bool uapsdEnable; +}; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; +}; + +#define TXOP_UNIT 32 +#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, life_time,\ + uapsd) \ + do { \ + struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ + p->cwMin = (cw_min); \ + p->cwMax = (cw_max); \ + p->aifns = (aifs); \ + p->txOpLimit = ((txop) * TXOP_UNIT); \ + p->maxReceiveLifetime = (life_time); \ + p->uapsdEnable = (uapsd); \ + } while (0) + +int wsm_set_edca_params(struct bes2600_common *hw_priv, + const struct wsm_edca_params *arg, int if_id); + +int wsm_set_uapsd_param(struct bes2600_common *hw_priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + /* [in] */ u8 channelMode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + /* [in] */ u8 channelSwitchCount; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + /* [in] */ u16 newChannelNumber; +}; + +int wsm_switch_channel(struct bes2600_common *hw_priv, + const struct wsm_switch_channel *arg, int if_id); + +typedef void (*wsm_channel_switch_cb) (struct bes2600_common *hw_priv); + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channelNumber; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 CTWindow; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beaconInterval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 DTIMPeriod; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* Length of the SSID */ + /* [in] */ u8 ssidLength; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basicRateSet; +}; + +int wsm_start(struct bes2600_common *hw_priv, const struct wsm_start *arg, + int if_id); + +#if 0 +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enableBeaconing; +}; + +int wsm_beacon_transmit(struct bes2600_common *hw_priv, + const struct wsm_beacon_transmit *arg, + int if_id); +#endif + +int wsm_start_find(struct bes2600_common *hw_priv, int if_id); + +int wsm_stop_find(struct bes2600_common *hw_priv, int if_id); + +typedef void (*wsm_find_complete_cb) (struct bes2600_common *hw_priv, + u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; + /* [out] */ int if_id; +}; + +typedef void (*wsm_suspend_resume_cb) (struct bes2600_vif *priv, + struct wsm_suspend_resume *arg); + +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct bes2600_common *hw_priv, + const struct wsm_update_ie *arg, int if_id); + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 unmap; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct bes2600_common *hw_priv, const struct wsm_map_link *arg, + int if_id); + +struct wsm_cbc { + wsm_scan_complete_cb scan_complete; + wsm_tx_confirm_cb tx_confirm; + wsm_rx_cb rx; + wsm_event_cb event; + wsm_set_pm_complete_cb set_pm_complete; + wsm_channel_switch_cb channel_switch; + wsm_find_complete_cb find_complete; + wsm_suspend_resume_cb suspend_resume; +}; +#ifdef MCAST_FWDING + +/* 3.65 Give Buffer Request */ +int wsm_init_release_buffer_request(struct bes2600_common *priv, u8 index); + +/* 3.67 Request Buffer Request */ +int wsm_request_buffer_request(struct bes2600_vif *priv, + u8 *arg); +#endif +/* ******************************************************************** */ +/* MIB shortcats */ +struct wsm_mib { + u16 mibId; + void *buf; + size_t buf_size; +}; + +static inline int wsm_set_output_power(struct bes2600_common *hw_priv, + int power_level, int if_id) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val), if_id); +} + +static inline int wsm_set_beacon_wakeup_period(struct bes2600_common *hw_priv, + unsigned dtim_interval, + unsigned listen_interval, + int if_id) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval)}; + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val), if_id); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct bes2600_common *hw_priv, + struct wsm_rcpi_rssi_threshold *arg, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg), if_id); +} + +struct wsm_counters_table { + __le32 countPlcpErrors; + __le32 countFcsErrors; + __le32 countTxPackets; + __le32 countRxPackets; + __le32 countRxPacketErrors; + __le32 countRxDecryptionFailures; + __le32 countRxMicFailures; + __le32 countRxNoKeyFailures; + __le32 countTxMulticastFrames; + __le32 countTxFramesSuccess; + __le32 countTxFrameFailures; + __le32 countTxFramesRetried; + __le32 countTxFramesMultiRetried; + __le32 countRxFrameDuplicates; + __le32 countRtsSuccess; + __le32 countRtsFailures; + __le32 countAckFailures; + __le32 countRxMulticastFrames; + __le32 countRxFramesSuccess; + __le32 countRxCMACICVErrors; + __le32 countRxCMACReplays; + __le32 countRxMgmtCCMPReplays; +}; + +static inline int wsm_get_counters_table(struct bes2600_common *hw_priv, + struct wsm_counters_table *arg) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct bes2600_common *hw_priv, u8 *mac) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_DOT11_STATION_ID, mac, + ETH_ALEN); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; + bool probeResponder; + bool keepalive; +}; + +static inline int wsm_set_rx_filter(struct bes2600_common *hw_priv, + const struct wsm_rx_filter *arg, + int if_id) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + if (arg->probeResponder) + val |= __cpu_to_le32(BIT(3)); + if (arg->keepalive) + val |= __cpu_to_le32(BIT(4)); + return wsm_write_mib(hw_priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val), + if_id); +} + +int wsm_set_probe_responder(struct bes2600_vif *priv, bool enable); +int wsm_set_keepalive_filter(struct bes2600_vif *priv, bool enable); + +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ieId; + u8 actionFlags; + u8 oui[3]; + u8 matchData[3]; +} __packed; + +struct wsm_beacon_filter_table { + __le32 numOfIEs; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct bes2600_common *hw_priv, + struct wsm_beacon_filter_table *ft, + int if_id) +{ + size_t size = __le32_to_cpu(ft->numOfIEs) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size, + if_id); +} + +#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ +#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct bes2600_common *hw_priv, + struct wsm_beacon_filter_control *arg, + int if_id) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val), if_id); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disableMoreFlagUsage; + int performAntDiversity; +}; + +static inline int wsm_set_operational_mode(struct bes2600_common *hw_priv, + const struct wsm_operational_mode *arg, + int if_id) +{ + u32 val = arg->power_mode; + if (arg->disableMoreFlagUsage) + val |= BIT(4); + if (arg->performAntDiversity) + val |= BIT(5); + return wsm_write_mib(hw_priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val), if_id); +} + +struct wsm_inactivity { + u8 max_inactivity; + u8 min_inactivity; +}; + +static inline int wsm_set_inactivity(struct bes2600_common *hw_priv, + const struct wsm_inactivity *arg, + int if_id) +{ + struct { + u8 min_inactive; + u8 max_inactive; + u16 reserved; + } val; + + val.max_inactive = arg->max_inactivity; + val.min_inactive = arg->min_inactivity; + val.reserved = 0; + + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_INACTIVITY, &val, + sizeof(val), if_id); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + bool disable; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct bes2600_common *hw_priv, + struct wsm_template_frame *arg, + int if_id) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + if (arg->disable) + ((u16 *) p)[1] = 0; + else + ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); + ret = wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, p, + arg->skb->len, if_id); + skb_pull(arg->skb, 4); + return ret; +} + + +struct wsm_protected_mgmt_policy { + bool protectedMgmtEnable; + bool unprotectedMgmtFramesAllowed; + bool encryptionForAuthFrame; +}; + +static inline int +wsm_set_protected_mgmt_policy(struct bes2600_common *hw_priv, + struct wsm_protected_mgmt_policy *arg, + int if_id) +{ + __le32 val = 0; + int ret; + if (arg->protectedMgmtEnable) + val |= __cpu_to_le32(BIT(0)); + if (arg->unprotectedMgmtFramesAllowed) + val |= __cpu_to_le32(BIT(1)); + if (arg->encryptionForAuthFrame) + val |= __cpu_to_le32(BIT(2)); + ret = wsm_write_mib(hw_priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, &val, + sizeof(val), if_id); + return ret; +} + +static inline int wsm_set_block_ack_policy(struct bes2600_common *hw_priv, + u8 blockAckTxTidPolicy, + u8 blockAckRxTidPolicy, + int if_id) +{ + struct { + u8 blockAckTxTidPolicy; + u8 reserved1; + u8 blockAckRxTidPolicy; + u8 reserved2; + } val = { + .blockAckTxTidPolicy = blockAckTxTidPolicy, + .blockAckRxTidPolicy = blockAckRxTidPolicy, + }; + return wsm_write_mib(hw_priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val), if_id); +} + +struct wsm_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preambleType; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfieldMode; /* 1 for greenfield */ + u8 mpduStartSpacing; + __le32 basicRateSet; +}; + +static inline int wsm_set_association_mode(struct bes2600_common *hw_priv, + struct wsm_association_mode *arg, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg), if_id); +} + +struct wsm_set_tx_rate_retry_policy_header { + u8 numTxRatePolicies; + u8 reserved[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy_policy { + u8 policyIndex; + u8 shortRetryCount; + u8 longRetryCount; + u8 policyFlags; + u8 rateRecoveryCount; + u8 reserved[3]; + __le32 rateCountIndices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + struct wsm_set_tx_rate_retry_policy_header hdr; + struct wsm_set_tx_rate_retry_policy_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct bes2600_common *hw_priv, + struct wsm_set_tx_rate_retry_policy *arg, + int if_id) +{ + size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) + + arg->hdr.numTxRatePolicies * + sizeof(struct wsm_set_tx_rate_retry_policy_policy); + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size, if_id); +} + +/* 4.32 SetEtherTypeDataFrameFilter */ +struct wsm_ether_type_filter_hdr { + u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_ether_type_filter { + u8 filterAction; /* WSM_FILTER_ACTION_XXX */ + u8 reserved; + __le16 etherType; /* Type of ethernet frame */ +} __packed; + +static inline int wsm_set_ether_type_filter(struct bes2600_common *hw_priv, + struct wsm_ether_type_filter_hdr *arg, + int if_id) +{ + size_t size = sizeof(struct wsm_ether_type_filter_hdr) + + arg->nrFilters * sizeof(struct wsm_ether_type_filter); + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, + arg, size, if_id); +} + + +/* 4.33 SetUDPPortDataFrameFilter */ +struct wsm_udp_port_filter_hdr { + u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_udp_port_filter { + u8 filterAction; /* WSM_FILTER_ACTION_XXX */ + u8 portType; /* WSM_FILTER_PORT_TYPE_XXX */ + __le16 udpPort; /* Port number */ +} __packed; + +static inline int wsm_set_udp_port_filter(struct bes2600_common *hw_priv, + struct wsm_udp_port_filter_hdr *arg, + int if_id) +{ + size_t size = sizeof(struct wsm_udp_port_filter_hdr) + + arg->nrFilters * sizeof(struct wsm_udp_port_filter); + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, + arg, size, if_id); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 categoryId; + u8 oui[4]; + __le16 subCategoryId; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devNameSize; + u8 localDevName[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 numSecDevSupported; + struct wsm_p2p_device_type secondaryDevices[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 WCDMA_Band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 GPIO_Command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 TSF_Counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 keepAlivePeriod; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct bes2600_common *hw_priv, + int period, int if_id) +{ + struct wsm_keep_alive_period arg = { + .keepAlivePeriod = __cpu_to_le16(period), + }; + return wsm_write_mib(hw_priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg), if_id); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct bes2600_common *hw_priv, + bool enabled, int if_id) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(hw_priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg), if_id); +} + +/* Multicat filtering - 4.5 */ +struct wsm_multicast_filter { + __le32 enable; + __le32 numOfAddresses; + u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +/* Mac Addr Filter Info */ +struct wsm_mac_addr_info { + u8 filter_mode; + u8 address_mode; + u8 MacAddr[6]; +} __packed; + +/* Mac Addr Filter */ +struct wsm_mac_addr_filter { + u8 numfilter; + u8 action_mode; + u8 Reserved[2]; + struct wsm_mac_addr_info macaddrfilter[0]; +} __packed; + +/* Broadcast Addr Filter */ +struct wsm_broadcast_addr_filter { + u8 action_mode; + u8 nummacaddr; + u8 filter_mode; + u8 address_mode; + u8 MacAddr[6]; +} __packed; + +static inline int wsm_set_multicast_filter(struct bes2600_common *hw_priv, + struct wsm_multicast_filter *fp, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp), if_id); +} + +/* ARP IPv4 filtering - 4.10 */ +struct wsm_arp_ipv4_filter { + __le32 enable; + __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; +} __packed; + +#ifdef IPV6_FILTERING +/* NDP IPv6 filtering */ +struct wsm_ndp_ipv6_filter { + __le32 enable; + struct in6_addr ipv6Address[WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES]; +} __packed; +/* IPV6 Addr Filter Info */ +struct wsm_ip6_addr_info { + u8 filter_mode; + u8 address_mode; + u8 Reserved[2]; + u8 ipv6[16]; +}; + +/* IPV6 Addr Filter */ +struct wsm_ipv6_filter { + u8 numfilter; + u8 action_mode; + u8 Reserved[2]; + struct wsm_ip6_addr_info ipv6filter[0]; +} __packed; +#endif /*IPV6_FILTERING*/ + +struct wsm_ip4_addr_info { + u8 filter_mode; + u8 address_mode; + u8 Reserved[2]; + u8 ipv4[4]; +}; + +/* IPV6 Addr Filter */ +struct wsm_ipv4_filter { + u8 numfilter; + u8 action_mode; + u8 Reserved[2]; + struct wsm_ip4_addr_info ipv4filter[0]; +} __packed; + +static inline int wsm_set_arp_ipv4_filter(struct bes2600_common *hw_priv, + struct wsm_arp_ipv4_filter *fp, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp), if_id); +} + +#ifdef IPV6_FILTERING +static inline int wsm_set_ndp_ipv6_filter(struct bes2600_common *priv, + struct wsm_ndp_ipv6_filter *fp, + int if_id) +{ + return wsm_write_mib(priv, WSM_MIB_ID_NS_IP_ADDRESSES_TABLE, + fp, sizeof(*fp), if_id); +} +#endif /*IPV6_FILTERING*/ + +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 oppPsCTWindow; + u8 count; + u8 reserved; + u8 dtimCount; + __le32 duration; + __le32 interval; + __le32 startTime; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct bes2600_common *hw_priv, + struct wsm_p2p_ps_modeinfo *mi, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi), if_id); +} + +static inline int wsm_get_p2p_ps_modeinfo(struct bes2600_common *hw_priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct bes2600_common *hw_priv, + bool enabled, int if_id) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(hw_priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg), if_id); +} + + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsdFlags; + __le16 minAutoTriggerInterval; + __le16 maxAutoTriggerInterval; + __le16 autoTriggerStep; +}; + +static inline int wsm_set_uapsd_info(struct bes2600_common *hw_priv, + struct wsm_uapsd_info *arg, + int if_id) +{ + /* TODO:COMBO:UAPSD will be supported only on one interface */ + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg), if_id); +} + +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int +wsm_set_override_internal_txrate(struct bes2600_common *hw_priv, + struct wsm_override_internal_txrate *arg, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg), if_id); +} +#ifdef MCAST_FWDING +/* 4.51 SetForwardingOffload */ +struct wsm_forwarding_offload { + u8 fwenable; + u8 flags; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_forwarding_offlad(struct bes2600_common *hw_priv, + struct wsm_forwarding_offload *arg,int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_FORWARDING_OFFLOAD, + arg, sizeof(*arg),if_id); +} + +#endif +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct bes2600_common *hw_priv); +void wsm_vif_lock_tx(struct bes2600_vif *priv); +void wsm_lock_tx_async(struct bes2600_common *hw_priv); +bool wsm_flush_tx(struct bes2600_common *hw_priv); +bool wsm_vif_flush_tx(struct bes2600_vif *priv); +void wsm_unlock_tx(struct bes2600_common *hw_priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct bes2600_common *hw_priv, u8 * data, size_t len); +int wsm_handle_rx(struct bes2600_common *hw_priv, int id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct bes2600_common *hw_priv, u8 **data, + size_t *tx_len, int *burst, int *vif_selected); +void wsm_txed(struct bes2600_common *hw_priv, u8 *data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queueId) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queueId]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queueId) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queueId]; +} + +#ifdef CONFIG_BES2600_TESTMODE +/** + * include signaling and nosignaling mode + */ +struct vendor_rf_cmd_t { + u32 cmd_type; + u32 cmd_argc; + u32 cmd_len; + u8 cmd[0]; +}; + +int wsm_vendor_rf_cmd(struct bes2600_common *hw_priv, int if_id, + const struct vendor_rf_cmd_t *vendor_rf_cmd); +int wsm_vendor_rf_cmd_confirm(struct bes2600_common *hw_priv, + void *arg, struct wsm_buf *buf); +int wsm_vendor_rf_test_indication(struct bes2600_common *hw_priv, struct wsm_buf *buf); + +#endif /* CONFIG_BES2600_TESTMODE */ + +/** + * bes2600 driver signaling and nosignaling cmd + */ +enum bes2600_rf_cmd_type { + BES2600_RF_CMD_CALI_TXT_TO_FLASH = 0, + BES2600_RF_CMD_CH_INFO = 1, + BES2600_RF_CMD_CPU_USAGE = 2, + BES2600_RF_CMD_WIFI_STATUS = 3, + /* add new here */ + + BES2600_RF_CMD_MAX, +}; + +int wsm_driver_rf_cmd_confirm(struct bes2600_common *hw_priv, + void *arg, struct wsm_buf *buf); + +struct wsm_epta_msg { + int wlan_duration; + int bt_duration; + int hw_epta_enable; +}; + +#define PROCTECT_MODE_RTS_CTS 0x1 /* Non retransmission frame with rts/cts procttion*/ +#define PROCTECT_MODE_CTS 0x2 /* Non retransmission frame with cts procttion */ +#define PROCTECT_MODE_RTS_CTS_RETRY 0x4 /* retransmission frame with rts/cts procttion */ + +typedef struct MIB_TXRX_OPT_PARAM_S +{ + /* if rts send ,no cts response, rts can retry max cnt = g_max_rts_retry_limit */ + u8 rts_retry_limit; + /* bit0: Non retransmission rts/cts protion. + * bit1: ALL(Non retransmission or retransmission)frame with cts PROTECT. + * bit2: retransmission frame with rts/cts PROTECT. + * support case1:Non retransmission frame with rts/cts PROTECT + * protect_mode = PROTECT_MODE_RTS_CTS + * support case2:ALL(Non retransmission or retransmission)frame with cts PROTECT + * protect_mode = PROTECT_MODE_CTS + * support case3:ALL(Non retransmission or retransmission)frame with rts PROTECT + * protect_mode = (PROTECT_MODE_RTS_CTS|PROTECT_MODE_RTS_CTS_RETRY) + * support case4:retransmission frame with rts/cts PROTECT + * protect_mode = (PROTECT_MODE_RTS_CTS_RETRY) + */ + u8 protect_mode; + u16 rts_duration; +} MIB_TXRX_OPT_PARAM; + + +int wsm_epta_cmd(struct bes2600_common *hw_priv, struct wsm_epta_msg *arg); + +int wsm_epta_wifi_chan_cmd(struct bes2600_common *hw_priv, uint32_t channel, uint32_t type); + +int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv); + +int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status); + +#ifdef PLAT_ALLWINNER_R329 +int wsm_save_factory_txt_to_flash(struct bes2600_common *hw_priv, const u8 *data, int if_id); +#endif + +#endif /* BES2600_HWIO_H_INCLUDED */ -- 2.34.1